Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug: react-hooks/exhaustive-deps - throws an unjustified warning for useEffect #31207

Open
sajera opened this issue Oct 12, 2024 · 5 comments
Open
Labels
Status: Unconfirmed A potential issue that we haven't yet confirmed as a bug

Comments

@sajera
Copy link

sajera commented Oct 12, 2024

A fairly simple and straightforward code sample is causing a warning. I might be wrong, and I apologize if that's the case, but this seems like a bug on your end.

React Hook useEffect received a function whose dependencies are unknown. Pass an inline function instead

React version: ^18.2.0

Steps To Reproduce

  1. Run lint on the code below.
const foo = { bar: () => console.log('Do something once at component mount') }

export default memo(function Example () {
  useEffect(foo.bar, [])
  return <div />
})

As you might guess, there is a function that should be triggered once when the component mounts. It returns undefined...

The current behavior

Throws the warning: React Hook useEffect received a function whose dependencies are unknown. Pass an inline function instead

The expected behavior

Not to throw

@sajera sajera added the Status: Unconfirmed A potential issue that we haven't yet confirmed as a bug label Oct 12, 2024
@xing24xing
Copy link

xing24xing commented Oct 13, 2024

Suggested Solution for react-hooks/exhaustive-deps Warning

I believe the issue is related to how react-hooks/exhaustive-deps interprets dependencies when using external object method. The warning arises because useEffect receives a function (foo.bar) that isn't declared inline, causing EsLint to think that it might change between renders.

Proposed Solution: Use an inline function

One way to resolve this is by using an inline function within useEffect:

  useEffect(() => {
    console.log('Do something once at component mount');
  }, []);
  return <div />;
});

This approach eliminates the warning because the function is defined directly inside useEffect and does not have unknown dependencies.

Alternative Solution: Use useCallback to Memoize the Function
If you prefer to use the method foo.bar, you can memoize it using

useCallback:


export default memo(function Example() {
  const stableBar = useCallback(foo.bar, []);
  useEffect(stableBar, []);
  return <div />;
});

This will ensure foo.bar is treated as a stable reference, avoiding unnecessary re-renders and warnings.

Let me know if this helps resolve the issue!

@Tanmayshi
Copy link

/assigned

@Tanmayshi
Copy link

You might have mismatching versions of React and the renderer (such as React DOM).
You might be breaking the Rules of Hooks.
You might have more than one copy of React in the same app.
Please refer to React's documentation for tips on how to debug and fix this problem.

It seems that in your case, you passed a named function (foo.bar) directly to useEffect, which caused React to have difficulty understanding the dependencies of that function.

The solution to this issue is to use an inline function or an arrow function instead. This will provide React with a clearer understanding of the dependencies, allowing it to manage the effect correctly.

@sajera
Copy link
Author

sajera commented Oct 14, 2024

@Tanmayshi - You're right. I know several ways to avoid this warning, but I think the rules are flawed. They shouldn't guess; they should help prevent errors or unexpected behavior. In my case, the code is clear and error-free, so this warning seems like a bug.

P.S. Your code example looks massive. I just don't like using that coding style ;)

P.S. 2 useCallback(foo.bar, []) is logically unexpected here and requires at least some additional explanation. useEffect(foo.bar, []) clearly indicates that we expect only the component's mount and unmount events.

@sajera
Copy link
Author

sajera commented Oct 14, 2024

BTW more samples of unexpected behavior based on your comments:

  1. useCallback may face the same problem as useEffect—why is the dependency check for it different compared to useEffect?
const foo = { bar: () => console.log('Do something once at component mount') }

const e1 = memo(function Example () {
  useEffect(foo.bar, []) // throw warning
  return <div />
})

const e2 = memo(function Example () {
  useCallback(foo.bar, []) // not to throw warning ?
  return <div />
})
  1. useEffect, from a rule perspective, behaves differently depending on the parent object of the function. Why is that?
const foo = () => console.log('Do something once at component mount')

const e1 = memo(function Example () {
  useEffect(foo, []) // not to throw warning ?
  return <div />
})

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Status: Unconfirmed A potential issue that we haven't yet confirmed as a bug
Projects
None yet
Development

No branches or pull requests

3 participants