im trying to create an array of user premissions, which can be controled by a set of checkboxes.
when i check a box and console.log()
the array it looks just fine with all the premissions i checked, but when i uncheck the box, the console.log()
returns the previus array value and it will be ubdated only on the next click
const [prems, setPrems] = useState([]);
const handleCheckbox = (e) => {
let arr = prems
if (e.target.checked) {
arr.push(e.target.value)
setPrems(arr)
console.log(prems) //shwos the array with all the checked values
} else {
let newArr = arr.filter((item) => {
return item !== e.target.value
})
setPrems(newArr)
console.log(prems) //shows the array still with the value i unchecked
}
};
the event on the checkboxes is onChange and it works the same with onClick
CodePudding user response:
React state is set asynchronously. So you can't rely on the state right after updating it.
In order to look for the changes of that state, it is better to put it inside a useEffect
hook and set the variable name as the dependency:
React.useEffect(() => {
// the prems have changed
}, [prems]);
Also you have a big mistake in your code:
let arr = prems
if (e.target.checked) {
arr.push(e.target.value)
...
Here, you are assigning the prems
variable to arr
and mutating the arr
variable. THEY REFERENCE THE SAME OBJECT! So pushing everything to arr
will also push it to prems
and vice versa. Never do that. Read more about JS objects here.
UPDATE: This is the correct code:
const handleCheckbox = (e) => {
if (e.target.checked) {
setPrems([ ...prems, e.target.value ])
} else {
const newPrems = prems.filter((item) => item !== e.target.value);
setPrems(newPrems)
}
};
React.useEffect(() => {
// do something
}, [prems]);
CodePudding user response:
You can reliably update the state using Functional updates in useState
:
const [prems, setPrems] = useState({});
const handleCheckbox = (e) => {
setPrems((prevState) => {
return { ...prevState, [e.target.value]: e.target.checked };
});
};
As @ Kamlesh sharma noted, state is async, so logging may not show the current state. You could instead render the prems
as follows to see the current state:
<ul>
{Object.keys(prems).map((prem) => {
if (prems[prem]) {
return <li key={prem}>{prem}</li>;
}
})}
</ul>
You can view the code in action with checkboxes in this JSFiddle.
EDIT:
If it is particularly important that prems
be an array rather than an object, the setPrems
call can be modified as follows:
setPrems((prevState) => {
if (e.target.checked) {
return [...prevState, e.target.value];
} else {
return prevState.filter((elem) => elem !== e.target.value);
}
});