I have a function called filterContactsByValue
. It is curried and takes in a value and a list of contacts and then filters the list based on the value and returns the (new) filtered list.
Since the list is often large (10.000+ entries), the web app should run on smartphones and the filter takes into account many values, I want to optimize the computing resources. Therefore I use useDebounce
to not compute unnecessarily.
I also used useCallback
like this to memoize the computation of the filteredContacts
:
function FilteredContacts({contacts}) {
const [filterParam, setFilterParam] = useState('');
const [value] = useDebounce(filterParam, 800);
const filterContacts = filterContactsByValue(value.toLowerCase());
// Is this okay? 🤔 ...
const getFilteredContacts = useCallback(() => filterContacts(contacts), [
value
]);
return (
<div className="main">
<SearchBar
value={filterParam}
onChangeText={setFilterParam}
/>
// ... and then this? 🧐
<ContactList contacts={getFilteredContacts()} />
</div>
);
}
I was wondering whether this is okay, or if returning values like this is bad practice. If it is bad, why and how would you improve it?
Edit:
The filterContactsByValue
function:
import { any, filter, includes, map, pick, pipe, toLower, values } from 'ramda';
import { stringFields } from './config/constants';
const contactIncludesValue = value =>
pipe(
pick(stringFields),
map(toLower),
values,
any(includes(value))
);
const filterContactsByValue = pipe(
contactIncludesValue,
filter
);
According to https://github.com/xnimorz/use-debounce you already have useDebouncedCallback
hook.
const getFilteredContacts = useDebouncedCallback(
() => filterContactsByValue(value.toLowerCase()),
800,
[value]
);
You can also use lodash's debounce or throttle (when you have lodash in your project), but as @skyboyer mentioned you may end with out-of-date callback version(s) will be run after appropriate delay
export {debounce} from 'lodash';
const getFilteredContacts = useCallback(
debounce(() => filterContactsByValue(value.toLowerCase()), 1000),
[value]
);
but useMemo
will be better option, because you don't really want function execution in your render method
const FilteredContacts = ({contacts}) => {
const [filterParam, setFilterParam] = useState('');
const [value] = useDebounce(filterParam, 800);
const contactsFilter = useMemo(
() => filterContactsByValue(value.toLowerCase()),
[value]
);
const filteredContacts = useMemo(
() => contactsFilter(contacts),
[value, contacts]
);
return (
<div className="main">
<SearchBar
value={filterParam}
onChangeText={setFilterParam}
/>
<ContactList contacts={filteredContacts} />
</div>
);
}