Skip to content

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

Open
@aweebit

Description

@aweebit

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 #31207 (comment).

Metadata

Metadata

Assignees

No one assigned

    Labels

    Status: UnconfirmedA potential issue that we haven't yet confirmed as a bug

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions