Skip to content

React hooks/set state in effect lint hir order fix#35988

Open
mugicaj wants to merge 3 commits intofacebook:mainfrom
mugicaj:react-hooks/set-state-in-effect_lint_HIR_order_fix
Open

React hooks/set state in effect lint hir order fix#35988
mugicaj wants to merge 3 commits intofacebook:mainfrom
mugicaj:react-hooks/set-state-in-effect_lint_HIR_order_fix

Conversation

@mugicaj
Copy link

@mugicaj mugicaj commented Mar 10, 2026

Summary

PR references this issue
The HIR evaluation in ValidateNoSetStateInEffect.ts suffers from evaluation order dependence. The validator has to:

  1. Recognize that the function passed to useEffectEvent contains setState and record that.
  2. When it sees useEffectEvent(callback), treat the return value of useEffectEvent as a function that calls setState and add it to setStateFunctions.
  3. When it sees useEffect(onSetState), see that onSetState is in setStateFunctions and report.

This means that if the callback for onSetState (which is a useEffectEvent function) is not evaluated by the time the useEffect hook is being evaluated, it will erroneously not throw "EffectSetState".

In order to solve this we can split the node discovery and the error reporting into 2 separate passes which ensures that by the time we are evaluating the errors we have a complete view of the HIR function, as opposed to reporting as we traverse it.

How did you test this change?

Test:

it('reports set-state-in-effect error when setState is called inside useEffect via useEffectEvent', () => {
  const logs: [string | null, LoggerEvent][] = [];
  const logger: Logger = {
    logEvent(filename, event) {
      logs.push([filename, event]);
    },
  };

  runBabelPluginReactCompiler(
    `import {useEffect, useEffectEvent, useState} from 'react';
    function Component() {
      const [, setState] = useState('');
      const onSetState = useEffectEvent(() => {
        setState('test');
      });
      useEffect(() => {
        onSetState();
      }, []);
      return null;
    }`,
    'test.js',
    'flow',
    {
      logger,
      panicThreshold: 'none',
      outputMode: 'lint',
      environment: {validateNoSetStateInEffects: true},
    },
  );

  const [, event] = logs.at(0)!;
  expect(event).toBeDefined();
  expect(event.kind).toBe('CompileError');
  expect(event.detail.category).toBe(ErrorCategory.EffectSetState);
});

@meta-cla meta-cla bot added the CLA Signed label Mar 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants