简体   繁体   中英

React : Checkbox stuck to true, but state is updated

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 })
    }

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 })
    }

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) IE 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM