react icon indicating copy to clipboard operation
react copied to clipboard

Bug: [eslint-plugin-react-hooks] No `react-hooks/exhaustive-deps` warning when an unstable reference is passed to hook's callback parameter

Open aweebit opened this issue 7 months ago • 6 comments

React version: 19.1.0 eslint-plugin-react-hooks version: 5.2.0

Steps To Reproduce

  1. git clone https://github.com/aweebit/eslint-plugin-react-hooks-unstable-reference-issue.git`
    cd eslint-plugin-react-hooks-unstable-reference-issue
    npm install
    
  2. Run npm run lint and observe how there are no warnings.
  3. Run npm start, open the served page in your browser and look at the console output.

Link to example repository: https://github.com/aweebit/eslint-plugin-react-hooks-unstable-reference-issue

Here is the full example code:

import { createElement, useEffect, useReducer } from "react";
import { createRoot } from "react-dom/client";

let bar = () => console.log("good");

function Example() {
  console.log("rendering");
  const [updateTrigger, triggerUpdate] = useReducer(() => ({}), {});

  useEffect(() => {
    const timeout = setTimeout(triggerUpdate);
    return () => clearTimeout(timeout);
  }, []);

  useEffect(bar, [updateTrigger]);

  return null;
}

createRoot(
  /** @type {HTMLDivElement} */ (document.getElementById("root"))
).render(createElement(Example));

setTimeout(() =>
  setTimeout(() => {
    console.log("changing bar");
    bar = () => console.log("evil");
  })
);

The current behavior

No ESLint warnings

The expected behavior

15:3  warning  React Hook useEffect received a function whose dependencies are unknown. Pass an inline function instead  react-hooks/exhaustive-deps

Details

The expected behavior is what happens if you write

const foo = { bar: () => console.log("good") };

and replace bar with foo.bar everywhere.

The warning makes perfect sense since there is no way for the linter plugin to know for sure foo.bar doesn't change between renders. But there is also no way for it to know it if a variable declared with let such as bar is passed! That's why a warning also has to be produced in that case.

On the other hand, if the variable passed was declared with const, it is fine to not produce any warning because in that case, the plugin would have a guarantee it's a stable reference.

The issue was discovered in https://github.com/facebook/react/issues/31207#issuecomment-2925440287.

aweebit avatar May 31 '25 17:05 aweebit

I want to use the opportunity and encourage everybody reading to have a look at #33041 and join in for a discussion. The issue has been completely ignored so far, which is unfortunate considering how often I run into situations where I really wish the functionality proposed there was provided by React itself. Happens pretty much in every project I work on.

aweebit avatar May 31 '25 18:05 aweebit

Hi is this issue open? I would like to contribute please assign it to me

maddie0501 avatar Jun 05 '25 10:06 maddie0501

This issue has been automatically marked as stale. If this issue is still affecting you, please leave any comment (for example, "bump"), and we'll keep it open. We are sorry that we haven't been able to prioritize it yet. If you have any new additional information, please include it with your comment!

github-actions[bot] avatar Sep 03 '25 10:09 github-actions[bot]

Bump

aweebit avatar Sep 03 '25 11:09 aweebit