Skip to content

feat(eslint-plugin): add no-fast-state rule#3699

Closed
travisbreaks wants to merge 1 commit into
pmndrs:masterfrom
travisbreaks:feat/no-fast-state
Closed

feat(eslint-plugin): add no-fast-state rule#3699
travisbreaks wants to merge 1 commit into
pmndrs:masterfrom
travisbreaks:feat/no-fast-state

Conversation

@travisbreaks

Copy link
Copy Markdown

Summary

  • Adds no-fast-state rule that warns when useState/useReducer setters are called inside useFrame callbacks
  • This is a common performance anti-pattern: state setters trigger full React re-renders every frame (60fps = 60 re-renders per second)
  • The correct pattern is useRef with direct mutation inside useFrame

Companion to prefer-useloader (#3698), covering another rule from the ESLint plugin RFC (#2701).

Implementation

  • Tracks useState and useReducer destructured setter names
  • Flags any call to a known setter inside a useFrame callback scope
  • Handles nested functions and conditional branches inside useFrame
  • Registered in both recommended and all configs via codegen
  • 11 test cases (5 valid, 6 invalid), 100% coverage on rule file
  • Docs following existing rule pattern

Test plan

  • All 21 eslint-plugin tests pass (3 suites)
  • 100% statement/branch/function/line coverage on all rule files
  • Codegen output verified (index, configs, README)

Example

// Bad: re-renders 60 times per second
const [pos, setPos] = useState([0, 0, 0])
useFrame(() => {
  setPos([Math.sin(clock.elapsedTime), 0, 0])
})

// Good: direct mutation, no re-renders
const meshRef = useRef()
useFrame(() => {
  meshRef.current.position.x = Math.sin(clock.elapsedTime)
})

Warns when React useState/useReducer setters are called inside useFrame
callbacks. This is a performance anti-pattern: state setters trigger full
React re-renders every frame (60fps = 60 re-renders/second). The correct
pattern is useRef with direct mutation.

- Tracks useState and useReducer destructured setter names
- Flags any call to a known setter inside a useFrame callback
- Registered in both recommended and all configs via codegen
- Comprehensive tests (5 valid, 6 invalid cases), 100% coverage
- Docs following existing rule pattern

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@codesandbox-ci

Copy link
Copy Markdown

This pull request is automatically built and testable in CodeSandbox.

To see build info of the built libraries, click here or the icon next to each commit SHA.

Latest deployment of this branch, based on commit 4559999:

Sandbox Source
example Configuration

@travisbreaks

Copy link
Copy Markdown
Author

Friendly ping. Happy to iterate on this if needed.

@travisbreaks

Copy link
Copy Markdown
Author

Closing due to no review activity. Happy to reopen if there's interest.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant