Problem
Hi,
I'm trying to partially update my state object :
const [elephantProp, setElephantProp] = useState({
name: "",
color: "",
size: "",
race: "",
country: "",
});
Let's say I want to update only color and size parameters.
What I have tried
1
Dynamically change parameters (according to the input object) :
const handleChange = (input) => {
Object.keys(input).forEach((key) => {
setElephantProp({ ...elephantProp, [key]: input[key] });
};
Output : change only last parameter (size).
2
Set all parameters at once :
const handleChange = (input) => {
setElephantProp({ ...elephantProp, color: input.color, size: input.size });
};
Output : Works but is not dynamic (if I add a parameter to the input object there will be no effects)
Do you have any ideas on how to dynamically update some of my state variables ?
Maybe I should consider splitting into multiple state variables (not convenient with a lot of variables) or using nested objects ?
Problem
Hi,
I'm trying to partially update my state object :
const [elephantProp, setElephantProp] = useState({
name: "",
color: "",
size: "",
race: "",
country: "",
});
Let's say I want to update only color and size parameters.
What I have tried
1
Dynamically change parameters (according to the input object) :
const handleChange = (input) => {
Object.keys(input).forEach((key) => {
setElephantProp({ ...elephantProp, [key]: input[key] });
};
Output : change only last parameter (size).
2
Set all parameters at once :
const handleChange = (input) => {
setElephantProp({ ...elephantProp, color: input.color, size: input.size });
};
Output : Works but is not dynamic (if I add a parameter to the input object there will be no effects)
Do you have any ideas on how to dynamically update some of my state variables ?
Maybe I should consider splitting into multiple state variables (not convenient with a lot of variables) or using nested objects ?
Share Improve this question edited Sep 15, 2020 at 16:23 skyboyer 23.8k7 gold badges62 silver badges71 bronze badges asked Sep 14, 2020 at 10:17 lucas24007lucas24007 1532 silver badges10 bronze badges 1- setElephantProp is Async method that's why this happening. When you are looping through it you not get the state updated value. so last value get updating. Can you please try the same with for loop it sync and forEach is Async. Hope it will help you. – Gaurang Doshi Commented Sep 14, 2020 at 10:22
5 Answers
Reset to default 4From you code I am going to assume that input
is an object representing the state updates, e.g.
{
name: "Dumbo",
country: "USA"
}
In this case you can simply do:
const handleChange = (input) => {
setElephantProp({ ...elephantProp, ...input });
}
As ...input
e last, its properties will overwrite elephantProp
properties that have the same key.
Please notice in this way all input
keys which are not present in elephantProp
, will be added to the state; e.g.
const elephantProp = {
name: "",
color: "",
size: "",
race: "",
country: "",
};
const input = {
name: "Dumbo",
country: "USA",
ableToFly: true,
};
const updatedElephantProp = {
...elephantProp,
...input
} // => the updated state will contain the key/value pair 'ableToFly: true' too
console.log(updatedElephantProp)
This behaviour might be desirable or not, depending on your needs and project specifications.
You can use concept useReducer
- is usually preferable to useState when you have plex state logic that involves multiple sub-values - useReducer
const [changes, onChange] = useReducer((value, change) => ({...(value || {}), ...change}), {});
//used
const {name, color, size, race, country} = changes;
const handleChange = change => {
//example change = {name: 'alex'}
onChange(change);
};
React remends using multiple states for the different variables.
However, if you did want to set a full object, you'd likely rather copy the object, and setState
only once. You loop might not be caught properly by the rerendering, as it is an async function.
I'm not sure what your implementation is, but something like this should work:
const handleChange = (objectIn) => {
// create a new object merging the old one and your input
const newElephant = {
...elephantProps,
...objectIn
}
setElephantProps(newElephant)
}
This should handle your case gracefully. If you're using multiple inputs, I usually handle it that way:
const onChange = ({ target: { value, name } }) => setElephant({
...elephant,
[name]: value
})
return <input onChange={onChange} name="size" value={elephant.size} />
You can pass 2-nd param to handleChange, for example:
const handleChange = (input, name) => {
setElephantProp({ ...elephantProp, [name]: input[name] });
};
One of your solutions here: codesandbox.io or bellow code using this approach.
const onChangeHandaller = (e) => {
setElephantProp({ ...elephantProp, [e.target.name]: e.target.value });
};
const [elephantProp, setElephantProp] = useState({
name: "",
color: "",
size: "",
race: "",
country: ""
});
const onChangeHandaller = (e) => {
setElephantProp({ ...elephantProp, [e.target.name]: e.target.value });
};
return (
<div className="App">
Name:{" "}
<input
value={elephantProp.name}
name="name"
onChange={(e) => onChangeHandaller(e)}
/>{" "}
<br />
Color:{" "}
<input
value={elephantProp.color}
name="color"
onChange={(e) => onChangeHandaller(e)}
/>{" "}
<br />
Size:{" "}
<input
value={elephantProp.size}
name="size"
onChange={(e) => onChangeHandaller(e)}
/>{" "}
<br />
Race:{" "}
<input
value={elephantProp.race}
name="race"
onChange={(e) => onChangeHandaller(e)}
/>{" "}
<br />
Country:{" "}
<input
value={elephantProp.country}
name="country"
onChange={(e) => onChangeHandaller(e)}
/>{" "}
<br />
</div>