Search code examples
reactjsstateusecallback

does `useCallback` have something like a `useRef.current` inside it that will ensure that calls always use the latest version of the callback?


I have a situation where I am loading several datasets; the user can choose how many to load. All the datasets are rendered on the same chart. The datasets load individually and asynchronously.

The code is something along the lines of

export const DatasetGraph = ({userSelections}) => {
    const [datasets, setDatasets] = useState({});
    // when a selection changes, update the data
    useEffect(() => {
        // while loading a dataset, it is visible but has no data
        setDatasets(userSelections.map(selec => ({ dsname: [] })));
        // trigger asynchronous loads for each new dataset
        userSelections.forEach((dsname) => fetchDataset(dsname));
    }, [userSelections]);

    const fetchDataset = async (dsname) => {
        response = await fetch(url + dsname);
        // QUESTION: after the await, will this call the most recent version of
        // the callback? Will it use the most recent datasets object or an old
        // one saved in the useCallback closure?
        updateDataset(dsname, response);
    };

    // when a fetch returns, update the empty placeholder with the real data
    const updateDataset = useCallback(
        // For this toy example, we could use a setState function to always
        // retrieve the latest `datasets`. However, this callback may use other
        // state which is really what this question is about.
        (dsname, response) => setDatasets({ ...datasets, dsname: response }),
        [datasets]
    );

    return <Graph data={datasets}></Graph>;
};

I have not yet tried just having each dataset be a React component that doesn't render anything to the DOM, which can then manage its own loading state. That might actually be easier.


Solution

  • useCallback uses the dependencies array to detect changes

    The useCallback method uses the dependencies array you pass to it in order to memoize the value of your function. Your function will be recreated every time but not assigned to updateDataset unless one of the dependencies has changed.

    You should be wary of using useCallback unless a component below your function is expensive to rerender, otherwise, useCallback won't have much of a positive effect on your application's performance if any positive effect at all.

    It works the same way that useMemo does to ensure that your data, in the case of useCallback it is a function, is only updated on your variable when something it depends on has changed.