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
