I'd like to perform a side-effect when some data changes, e.g.
const useSearchResults = () => {
const location = useLocation();
const [data, setData] = useState();
useEffect(() => {
Api.search(location.query.q).then(data => setData(data));
}, [location.query.q]);
// Compute some derived data from `data`.
const searchResults =
data &&
data.map(item => ({
id: item.id,
name: `${item.firstName} ${item.lastName}`
}));
return searchResults;
};
const Component = () => {
const searchResults = useSearchResults();
useEffect(() => {
alert('Search results have changed'); // Side-effect
}, [searchResults]);
return <pre>{JSON.stringify(searchResults)}</pre>;
};
If something causes Component
to re-render, the alert
will fire even if the searchResults
haven't changed because we map over the underlying stable data in useSearchResults
creating a new instance on every render.
My initial approach would be to use useMemo
:
// Stabilise `searchResults`'s identity with useMemo.
const searchResults = useMemo(
() =>
data &&
data.map(item => ({
id: item.id,
name: `${item.firstName} ${item.lastName}`
})),
[data]
);
However useMemo
has no semantic guarantee so it's (theoretically) only good for performance optimisations.
Does React offer a straight forward solution to this (common?) problem?