I have a sort handler defined like so:
const onSortHandler = useCallback((property: string, direction: string) => {
setTodoList([...todoList.sort(/**<sort function snipped>**/)]);
}, [todoList]);
This function is passed down to a child component where I have something like:
const [sort, setSort] = useState<{ name: string | undefined, direction: string | undefined }>({ name: 'id', direction: 'asc' })
useEffect(() => {
onSortHandler(sort.name, sort.direction);
}, [sort, onSortHandler]);
The idea being that whenever someone changes the sort property and direction, I trigged a new sort and spreading the new object into setTodoList
causes a rerender.
Now this works fine, but eslint told me that I should add onSortHandler
to the useEffect
dependencies array, which I did, and it caused a massive amount of renders, so I followed it's advice and added useCallback
into the mix.
So I get a huge amount of renders, making the sort functionality not work. If I remove onSortHandler
from the useEffect
dependency array, it works fine but I'm getting the eslint warning I'd like to sort.
I know that it's the spread causing the issue as if I remove it, the issue goes away (but then the list never rerenders on a new sort which is another problem).
CodePudding user response:
Make onSortHandler
stable (meaning it never changes) like:
const onSortHandler = useCallback((property: string, direction: string) => {
// use functional state update:
setTodoList(current => current.slice().sort(/**<sort function snipped>**/));
}, []); // < no dependencies
This allows you to use it as a dependency wherever necessary without it actually triggering any update effects.
Note: I changed the spread to slice
(make a copy first), because using .sort
on current
(or todoList
in your code) is still a state manipulation, as sort
sorts in place.
Ref: Functional updates