Skip to content

[eslint-plugin-react-hooks] Treat useActionState dispatch as stable when isPending is destructured#36683

Open
sarathfrancis90 wants to merge 1 commit into
facebook:mainfrom
sarathfrancis90:fix/exhaustive-deps-useactionstate-ispending
Open

[eslint-plugin-react-hooks] Treat useActionState dispatch as stable when isPending is destructured#36683
sarathfrancis90 wants to merge 1 commit into
facebook:mainfrom
sarathfrancis90:fix/exhaustive-deps-useactionstate-ispending

Conversation

@sarathfrancis90
Copy link
Copy Markdown

Summary

exhaustive-deps treats the dispatch/setter returned by useState/useReducer/useActionState as a stable value, so you don't have to list it in a dependency array. But the stability check only fired when the hook result was destructured into exactly two elements. useActionState returns three: [state, dispatch, isPending]. As soon as you destructure the isPending element, the rule stopped recognizing dispatch as stable and reported a false positive:

const [state, dispatch, isPending] = useActionState(action, 0);
useEffect(() => {
  dispatch();
}, []); // ⚠️ React Hook useEffect has a missing dependency: 'dispatch'

The two-element form [state, dispatch] works fine, which is what made this easy to miss. Same false positive as #32062.

Fix

Allow the three-element tuple form specifically for useActionState (useState/useReducer stay restricted to two). The existing branch logic already handles the rest correctly: dispatch is stable, while state and isPending stay dynamic and must still be listed.

Test plan

Added valid cases for the [state, dispatch, isPending] form (both useActionState and React.useActionState) and an invalid case confirming state/isPending are still required. jest passes for the full eslint-plugin-react-hooks suite (3680 tests).

…ith isPending

The exhaustive-deps rule only recognized the dispatch function returned
by useState/useReducer/useActionState as stable when the result was
destructured into a two-element tuple. However, useActionState returns a
three-element tuple, [state, dispatch, isPending], and destructuring the
isPending element caused the rule to stop treating dispatch as stable.

As a result, code like:

  const [state, dispatch, isPending] = useActionState(action, 0);
  useEffect(() => {
    dispatch();
  }, []);

produced a false positive: "React Hook useEffect has a missing
dependency: 'dispatch'".

Allow the three-element tuple form for useActionState while keeping
useState and useReducer restricted to two elements. state and isPending
remain dynamic.
@meta-cla meta-cla Bot added the CLA Signed label Jun 3, 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.

1 participant