I've a value in my state to store a list of cities, a checkbox to select them in a table, and an event listener to add the selected value to the state.
I can toggle the checkbox one time, from false to true. Then, it can't toggle to false. I'm logging the state and it's correctly updated, so I don't understand why my checkbox isn't updated.
For example :
- The "New York" check box isn't checked, so state is
[]
- I can check it, the state changes to
["New York"]
- When I uncheck it, the state changes to
[]
, but nothing happens in the checkbox
Here's the code :
const [selected, setSelected] = useState([]);
<input type="checkbox" onChange={(e) => handleRowSelect(e, bpf.city_name)} checked={selected.indexOf(bpf.city_name) > -1} />
const handleRowSelect = (e, city) => {
let arr = selected;
const index = arr.indexOf(city)
if (index === -1) {
arr.push(city);
} else {
arr.splice(index, 1);
}
setSelected(arr);
console.log(arr)
console.log(selected)
dispatch({ type: 'SET_SEL_BPF', payload: selected })
}
CodePudding user response:
This is because you are mutating your state selected
.
The line let arr = selected;
does not deep copy
the selected
State. arr
and selected
actually point to the same object in memory.
This means selected
is being mutated and not updated with a completely new State (aka mutated) I.E. changing the values of the same object instead of creating a completely new obj with it's new values.
When you mutate your state. React does not update properly and it is a big rule in react to never mutate your state.
Look into making a deep copy of your state and then mutating that deep copy
and then setting selected to that new deep copy setSelected(*your deep copy*)
Edit: Looking at the other answers BEWARE of creating a copy like let arr = [...selected]
. This will work if the array is only one level deep. But if you have an array of objects like: let arr = [{}]
or nested arrays let arr = [[]]
the objects or arrays inside {}
won't be deep copies and will be mutated. Causing the same issue.
CodePudding user response:
Your problem is object immutability in React. Whenever you set a new state, you need to initialize a new object/array to make React understand that your object/array has been changed.
In your case, you're using arr.push
and arr.splice
which maintain the same array for the whole time
The change can be
const handleRowSelect = (e, city) => {
let arr = selected;
const index = arr.indexOf(city)
if (index === -1) {
//create a new array, and add city
arr = [...arr, city]
} else {
//create a new array with `filter`
arr = arr.filter((city, index) => index !== arr.length - 1)
}
setSelected(arr);
console.log(arr)
console.log(selected)
dispatch({ type: 'SET_SEL_BPF', payload: selected })
}
Or you can try this way with a new array initialisation let arr = [...selected];
const handleRowSelect = (e, city) => {
let arr = [...selected]; //create a new array from the existing one
const index = arr.indexOf(city)
if (index === -1) {
arr.push(city);
} else {
arr.splice(index, 1);
}
setSelected(arr);
console.log(arr)
console.log(selected)
dispatch({ type: 'SET_SEL_BPF', payload: selected })
}