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>