I'm trying to apply a lodash throttle for the first time.
I know that the throttle has to be applied inside of a useCallback
or it will be called every re-render (in my case, with every new keystroke of a user search).
The code I have is valid, and the logic seems to make sense - but the throttle isn't being applied, and so the api call is being made every single keystroke.
Any pointers as to where my logic is failing?
import {
useEffect,
useCallback
} from 'react';
import { throttle } from 'lodash';
import { getAllUsers } from '../../../api/api';
import { USER_ROLE } from '../../../types/types'
interface IProps extends Omit<unknown, 'children'> {
search?: string;
}
const DemoFanManagementTable = ({ search }: IProps): JSX.Element => {
const getFans = (search?: string) => {
console.log("getFans ran")
const fans = getAllUsers({ search }, USER_ROLE.FAN);
//logs a promise
console.log("logging fans ", fans)
return fans;
}
//throttledSearch is running every time search changes
const throttledSearch = useCallback((search?: string) => {
console.log("throttledSearch ran")
return throttle(
//throttle is not throttling, functions run every keystroke
() => {
getFans(search), 10000, { leading: true, trailing: true }
}
)
}, [search])
//useEffect is running every time search changes
useEffect(() => {
return throttledSearch(search)
}, [search]);
return (
<div>
{search}
</div>
);
};
export default DemoFanManagementTable;
CodePudding user response:
There are a few problems here, first you have wrapped the hole throttle
func in an anonymous function instead of just the first param:
throttle(
(search: string) => getFans(search),
1000,
{ leading: true, trailing: true }
)
Second useCallback
is not suitable as each time you call it, it's returning a new throttled function.
Third you have passed [search]
as a dependency of useCallback
so even if it worked as you expected, it would be invalidated each time search
changes and not work anyway.
A better choice is useMemo
as it keeps the same throttled function across renders.
const throttledSearch = useMemo(
() =>
throttle(
(search: string) => getFans(search),
10000,
{ leading: true, trailing: true }
),
[]
);
useEffect(() => {
return throttledSearch(search);
}, [search]);
Since getFans takes the same search param you can shorten it to:
const throttledSearch = useMemo(() =>
throttle(getFans, 10000, { leading: true, trailing: true }),
[]);