facebook / react

The library for web and native user interfaces.
https://react.dev
MIT License
228.7k stars 46.81k forks source link

[eslint-plugin-react-hooks] Exhaustive deps requires a dispatch function from useReducer to be included in dependencies #18396

Closed maxijonson closed 4 years ago

maxijonson commented 4 years ago

The eslint-plugin-react-hooks does not seem to recognize the dispatch function from useReducer when it is returned from another function. If we use that returned function in a useCallback or useEffect, the plugin warns that the function should be added to the dependency list.

As per React docs:

React guarantees that `dispatch` function identity is stable and won’t change on re-renders. This is why it’s safe to omit from the `useEffect` or `useCallback` dependency list.

React version: 16.12.0 eslint-plugin-react-hooks version: 2.5.1

Steps To Reproduce

  1. Create a hook that returns the dispatch function of useReducer
  2. Use that returned function in useCallback or useEffect
const useForceUpdate = () => {
    const [, forceUpdate] = useReducer((x) => x + 1, 0);
    return forceUpdate;
};

const Component = () => {
    const forceUpdate = useForceUpdate();

    const onClick = useCallback(() => {
        console.log("CLICKED!");
        forceUpdate();
    }, []);
    // ^ missing dependency forceUpdate

    const [, forceUpdate2] = useReducer((x) => x + 1, 0);

    const onClick2 = useCallback(() => {
        console.log("CLICKED!");
        forceUpdate2();
    }, []);
    // No warning

    return (
        <>
            <button onClick={onClick} />
            <button onClick={onClick2} />
        </>
    );
};

The current behavior

There is a warning on onClick

React Hook React.useCallback has a missing dependency: 'forceUpdate'. Either include it or remove the dependency array. eslint(react-hooks/exhaustive-deps)

The expected behavior

There shouldn't be a warning, since useForceUpdate returns a dispatch function from useReducer.

gaearon commented 4 years ago

This is intentional because a linter can only verify so much statically.

There's no harm to passing this function if it's truly stable.