react: Bug: react-hooks/exhaustive-deps complains about useRef encapsulated in custom hook
So I’m using a custom usePrevious hook that returns the ‘previous value’ which is as far as I know pretty common practice. It leverages a ref inside the hook. When I declare a ref inside a functional component and use this in a useEffect, react-hooks/exhaustive-deps does not require me to add the ref to the dependencies list of a useEffect, but using usePrevious does.
React version: "react": "^17.0.1",
Steps To Reproduce
Link to code example: https://codepen.io/spassvogel/pen/JjbXKJL?editors=1111
const usePrevious = (value) => {
const ref = React.useRef();
React.useEffect(() => {
ref.current = value;
}, [value]);
return ref.current;
};
const TestComponent = () => {
const [state, setState] = React.useState(0);
const previousState = usePrevious(state)
React.useEffect(() => {
console.log('current state', state)
console.log('previous state', previousState)
}, [state]); // ESLINT COMPLAINS
return null;
};
The current behavior
react-hooks/exhaustive-deps complains about the dependency list of useEffect is missing previousState.
The expected behavior
react-hooks/exhaustive-deps realizes that usePrevious is leveraging useRef and thus doesn’t need its return value in the dependency list.
A workaround would be to duplicate the code of usePrevious in every component, then react-hooks/exhaustive-deps does not complain. but yeah…
About this issue
- Original URL
- State: closed
- Created 3 years ago
- Reactions: 10
- Comments: 15
https://github.com/facebook/react/pull/20477
Looks like this PR would do it?
Would usePrevious need to return the ref instead of ref.current, since current will be changed on the render after every update? Seems wrong to ignore a value that actually is changing just as a matter of principle.
I understand the problem and sympathize with it, but the canonical solution that scales is that you simply include that as a dependency. Since it’s static and never changes, there is no issue including it. On the other hand, if you later do return something conditionally, like
cond ? ref1 : ref2, then it will trigger a re-render instead of silently using a stale value. So this works precisely as designed.Maybe a quick solution would be would be to add a configuration option to the linting rule where you can name what other hooks should be treated the same way as
useRef. That way you could addusePreviousto the eslint config in your project and you’d effectively get the same behavior.