Home > Software engineering >  array updates in delay on checkbox click
array updates in delay on checkbox click

Time:02-26

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);
  }
});
  • Related