Skip to content

Bug: react-hooks/set-state-in-effect overly strict #34743

@karlhorky

Description

@karlhorky

eslint-plugin-react-hooks version: 6.1.1

Steps To Reproduce

  1. Use common, valid patterns for setting state in an effect, from those in the wild and official docs (see patterns in section below)
  2. Problem reported by eslint-plugin-react-hooks 💥

Link to code example: https://codesandbox.io/p/devbox/still-resonance-lzsl44?file=%2Fapp%2FComponent.tsx%3A10%2C49&workspaceId=ws_GfAuHrswXyA1DoeSwsjjjz

Image
$ pnpm eslint . --max-warnings 0

/project/workspace/app/Component.tsx
  7:5  error  Error: Calling setState synchronously within an effect can trigger cascading renders

Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:
* Update external systems with the latest state from React.
* Subscribe for updates from some external system, calling setState in a callback function when external state changes.

Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).

/project/workspace/app/Component.tsx:7:5
   5 |
   6 |   useEffect(() => {
>  7 |     setDidMount(true);
     |     ^^^^^^^^^^^ Avoid calling setState() directly within an effect
   8 |   }, []);
   9 |
  10 |   return <div>Mounted: {didMount ? "Yes" : "No"}</div>;  react-hooks/set-state-in-effect

✖ 1 problem (1 error, 0 warnings)

Widespread setState in useEffect Patterns

  1. Displaying different content on the server and client aka didMount, isClient, isMounted, mounted React docs 1, React docs 2, Next.js docs, next-themes docs, MUI Joy UI docs
  2. Setting a value while avoiding cascading renders from @controversial
    [Compiler Bug]: False positives “calling setState synchronously within an effect body” with stable primitive values #34045

I will add any more common patterns I find to the list as I encounter more examples.

The current behavior

react-hooks/set-state-in-effect raises a problem with the message Calling setState synchronously within an effect can trigger cascading renders

The expected behavior

react-hooks/set-state-in-effect is less strict, allowing for common usage patterns while catching possible "effect loops" or other unwanted behavior

Alternatives considered

  1. Improved examples on the react-hooks/set-state-in-effect docs page, showing more patterns such as the remediation patterns mentioned below that can be used in lieu of setting state directly in an effect (possible downside: some patterns may not have usable replacements outside of setting state in effects)
  2. Improved examples on the react-hooks/set-state-in-effect docs page, showing alternative patterns with setting state directly in an effect eg. using startTransition(), requestAnimationFrame() or setTimeout() (downside: seems wrong, like a code smell or "tricking the lint rule")

History

cc @jedwards1211 @josephsavona

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