How To Add Multiple Objects In Reactjs?
Solution 1:
It is a lot of code because I've added set and get to set and get state. Now you can store the path to the state in permissionsKey
and topGroupKey
. You can put get and set in a separate lib.js.
In this example Row is pretty much stateless and App holds it's state, this way App can do something with the values once the user is finished checking/unchecking what it needs.
constCheckbox = antd.Checkbox;
constCheckboxGroup = Checkbox.Group;
classRowextendsReact.Component {
isAllChecked = () => {
const { options, checked } = this.props;
return checked.length === options.length;
};
isIndeterminate = () => {
const { options, checked } = this.props;
return (
checked.length > 0 && checked.length < options.length
);
};
render() {
const {
options,
checked,
onChange,
onToggleAll,
stateKey,
label,
} = this.props; //all data and behaviour is passed by Appreturn (
<div><divclassName="site-checkbox-all-wrapper"><Checkboxindeterminate={this.isIndeterminate()}onChange={e =>
onToggleAll(e.target.checked, stateKey)
}
checked={this.isAllChecked()}
>
Check all {label}
</Checkbox><CheckboxGroupoptions={options}value={checked}onChange={val => {
onChange(stateKey, val);
}}
/>
</div></div>
);
}
}
//helper from https://gist.github.com/amsterdamharu/659bb39912096e74ba1c8c676948d5d9constREMOVE = () => REMOVE;
constget = (object, path, defaultValue) => {
constrecur = (current, path) => {
if (current === undefined) {
return defaultValue;
}
if (path.length === 0) {
return current;
}
returnrecur(current[path[0]], path.slice(1));
};
returnrecur(object, path);
};
constset = (object, path, callback) => {
constsetKey = (current, key, value) => {
if (Array.isArray(current)) {
return value === REMOVE
? current.filter((_, i) => key !== i)
: current.map((c, i) => (i === key ? value : c));
}
return value === REMOVE
? Object.entries(current).reduce((result, [k, v]) => {
if (k !== key) {
result[k] = v;
}
return result;
}, {})
: { ...current, [key]: value };
};
constrecur = (current, path) => {
if (path.length === 1) {
returnsetKey(
current,
path[0],
callback(current[path[0]])
);
}
returnsetKey(
current,
path[0],
recur(current[path[0]], path.slice(1))
);
};
returnrecur(object, path, callback);
};
classAppextendsReact.Component {
state = {
permission: { group: [] },
topGroup: [],
some: { other: [{ nested: { state: [] } }] },
};
permissionsKey = ['permission', 'group']; //where to find permissions in state
topGroupKey = ['topGroup']; //where to find top group in state
someKey = ['some', 'other', 0, 'nested', 'state']; //where other group is in state
onChange = (key, value) => {
//use set helper to set statethis.setState(set(this.state, key, arr => value));
};
isIndeterminate = () =>
!this.isEverythingChecked() &&
[
this.permissionsKey,
this.topGroupKey,
this.someKey,
].reduce(
(result, key) =>
result || get(this.state, key).length,
false
);
toggleEveryting = e => {
const checked = e.target.checked;
this.setState(
[
this.permissionsKey,
this.topGroupKey,
this.someKey,
].reduce(
(result, key) =>set(result, key, () =>
checked
? this.plainOptions.map(({ value }) => value)
: []
),
this.state
)
);
};
onToggleAll = (checked, key) => {
this.setState(
//use set helper to set stateset(this.state, key, () =>
checked
? this.plainOptions.map(({ value }) => value)
: []
)
);
};
isEverythingChecked = () =>
[
this.permissionsKey,
this.topGroupKey,
this.someKey,
].reduce(
(result, key) =>
result &&
get(this.state, key).length ===
this.plainOptions.length,
true
);
plainOptions = [
{ value: 1, name: 'Apple' },
{ value: 2, name: 'Pear' },
{ value: 3, name: 'Orange' },
];
render() {
return (
<React.Fragment><h1>App state</h1>
{JSON.stringify(this.state)}
<div><Checkboxindeterminate={this.isIndeterminate()}onChange={this.toggleEveryting}checked={this.isEverythingChecked()}
>
Toggle everything
</Checkbox></div>
{[
{ label: 'group', stateKey: this.permissionsKey },
{ label: 'top', stateKey: this.topGroupKey },
{ label: 'other', stateKey: this.someKey },
].map(({ label, stateKey }) => (
<Rowkey={label}options={this.plainOptions}
// usegettertogetstateselectedvalue
// forthisparticulargroupchecked={get(this.state,stateKey)}
label={label}onChange={this.onChange} //changebehaviourfromApponToggleAll={this.onToggleAll} //toggleallfromApp
//statekeytoindicatewhatstateneedstochange
// usedinsetStateinAppandpassedtosethelperstateKey={stateKey}
/>
))}
</React.Fragment>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
<linkhref="https://cdnjs.cloudflare.com/ajax/libs/antd/4.0.3/antd.css"rel="stylesheet"/><scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script><scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script><scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/antd/4.0.3/antd.js"></script><divid="root"></div>
Solution 2:
I rewrite all the handlers.
The bug in your code is located on the usage of antd Checkbox.Group
component with map
as a child component
, perhaps we need some key
to distinguish each of the Row
. Simply put them in one component works without that strange state update.
As the demand during communication, the total button
is also added.
And, we don't need many states, keep the single-source data is always the best practice.
importReactfrom"react";
importReactDOM from"react-dom";
import"antd/dist/antd.css";
import"./index.css";
import { Checkbox } from"antd";
const group = ["group", "top"];
const groupItems = ["Apple", "Pear", "Orange"];
constCheckboxGroup = Checkbox.Group;
classAppextendsReact.Component {
constructor() {
super();
this.state = {
permission: {}
};
}
UNSAFE_componentWillMount() {
this.setDefault(false);
}
setDefault = fill => {
const temp = {};
group.forEach(x => (temp[x] = fill ? groupItems : []));
this.setState({ permission: temp });
};
checkLength = () => {
const { permission } = this.state;
let sum = 0;
Object.keys(permission).forEach(x => (sum += permission[x].length));
return sum;
};
/**
* For total
*/
isTotalIndeterminate = () => {
const len = this.checkLength();
return len > 0 && len < groupItems.length * group.length;
};
onCheckTotalChange = () =>e => {
this.setDefault(e.target.checked);
};
isTotalChecked = () => {
returnthis.checkLength() === groupItems.length * group.length;
};
/**
* For each group
*/
isIndeterminate = label => {
const { permission } = this.state;
return (
permission[label].length > 0 &&
permission[label].length < groupItems.length
);
};
onCheckAllChange = label =>e => {
const { permission } = this.state;
const list = e.target.checked ? groupItems : [];
this.setState({ permission: { ...permission, [label]: list } });
};
isAllChecked = label => {
const { permission } = this.state;
return !groupItems.some(x => !permission[label].includes(x));
};
/**
* For each item
*/
isChecked = label => {
const { permission } = this.state;
return permission[label];
};
onChange = label =>e => {
const { permission } = this.state;
this.setState({ permission: { ...permission, [label]: e } });
};
render() {
const { permission } = this.state;
console.log(permission);
return (
<React.Fragment><Checkboxindeterminate={this.isTotalIndeterminate()}onChange={this.onCheckTotalChange()}checked={this.isTotalChecked()}
>
Check all
</Checkbox>
{group.map(label => (
<divkey={label}><divclassName="site-checkbox-all-wrapper"><Checkboxindeterminate={this.isIndeterminate(label)}onChange={this.onCheckAllChange(label)}checked={this.isAllChecked(label)}
>
Check all
</Checkbox><CheckboxGroupoptions={groupItems}value={this.isChecked(label)}onChange={this.onChange(label)}
/></div></div>
))}
</React.Fragment>
);
}
}
ReactDOM.render(<App />, document.getElementById("container"));
Try it online:
Solution 3:
Please try this,
onChange = value =>checked => {
this.setState({ checked }, () => {
this.setState(prevState => {
permission : { ...prevSatate.permission , { [value]: this.state.checked }}
});
});
};
by using spread operator you can stop mutating the object. same way you can also use object.assign like this.
this.setState(prevState => {
permission : Object.assign({} , prevState.permission, { [value]: this.state.checked });
});
And also i would suggest not to call setState in a callback. If you want to access the current state you can simply use the current checked
value which you are getting in the function itself.
so your function becomes ,
onChange = value =>checked => {
this.setState({ checked });
this.setState(prevState => {return { permission : { ...prevSatate.permission, { [value]: checked }}
}});
};
Solution 4:
Try the following
//Inside constructor do the followingthis.state = {checkState:[]}
this.setChecked = this.setChecked.bind(this);
//this.setChecked2 = this.setChecked2.bind(this);//Outside constructor but before render()
setChecked(e){
this.setState({
checkState : this.state.checkState.concat([{checked: e.target.id + '=>' + e.target.value}])
//Id is the id property for a specific(target) field
});
}
//Finally attack the method above.i.e. this.setChecked to a form input.
Hope it will address your issues
Post a Comment for "How To Add Multiple Objects In Reactjs?"