Home > Enterprise >  Problem using useState inside useCallback hook
Problem using useState inside useCallback hook

Time:04-11

I'm having a problem where a useState hook "setSelectAll(!selectAll)" inside a useCallback function updates the value but the conditional below that sentence is using the old selectAll value, so i have to make two clicks to make it work, and I dont know why, any help wold be very aprreciated !

I have this useCallback hook that executes when I press a Button

<Button  onClick={onSelectAll}>{selectAll ? t`Deselect All` : t`Select All`}</Button>

And here's my callback,

const onSelectAll = useCallback(() => {
  setSelectAll(!selectAll);
  if(selectAll) {  
    let newItemsSelected = [...itemsSelected];
    arrayList.forEach(item => {
      newItemsSelected.push(item.id);
    })
    setItemsSelected(newItemsSelected);
    onSelectedChange(newItemsSelected);
  } else {
    setItemsSelected([]);
  }
}, [selectAll, itemsSelected]);```

CodePudding user response:

To avoid having React lifecycle issues in cases like this, you'll need to set the state based off the previous state.

Like this:

setSelectAll(prevSelectAllState => !prevSelectAllState);

Here's what it would look like in your exact example:

const onSelectAll = useCallback(() => {
  setSelectAll(prevSelectAllState => !prevSelectAllState);
  if(newSelectAll) {  
    let newItemsSelected = [...itemsSelected];
    arrayList.forEach(item => {
      newItemsSelected.push(item.id);
    })
    setItemsSelected(newItemsSelected);
    onSelectedChange(newItemsSelected);
  } else {
    setItemsSelected([]);
  }
}, [selectAll, itemsSelected]);

CodePudding user response:

You are mutating state directly which will cause issues, You need to let react update state, like below sample

setSelectAll(prevState => !prevState)

CodePudding user response:

use/setState is synchronous, so any code running directly after it will not see the result of the changed state until after the component re-renders. Instead, just simulate the change by using an altered version before the real one changes:

const onSelectAll = useCallback(() => {
  const newSelectAll = !selectAll; // <--
  setSelectAll(newSelectAll);
  if(newSelectAll) {  
    let newItemsSelected = [...itemsSelected];
    arrayList.forEach(item => {
      newItemsSelected.push(item.id);
    })
    setItemsSelected(newItemsSelected);
    onSelectedChange(newItemsSelected);
  } else {
    setItemsSelected([]);
  }
}, [selectAll, itemsSelected]);

CodePudding user response:

You need to understand lifecycle of React component.

useEffect will be fired after state change in re-render by updated state. And useCallback function is also changed.

So you can call your callback function internally without direct call by changing your state.

const onSelectAll = useCallback(() => {
  if(selectAll) {  
    let newItemsSelected = [...itemsSelected];
    arrayList.forEach(item => {
      newItemsSelected.push(item.id);
    })
    setItemsSelected(newItemsSelected);
    onSelectedChange(newItemsSelected);
  } else {
    setItemsSelected([]);
  }
}, [selectAll, itemsSelected]);

<Button  onClick={setSelectAll(prevSelectAllState => !prevSelectAllState);}>{selectAll ? t`Deselect All` : t`Select All`}</Button>


  • Related