·
Last changedVersion:
This version passed the hardening gate. Hardening applied 8 targeted changes and preserved all other text. Classified findings before hardening: 0. Classified findings closed: 0. Classified findings remaining: 0. Unclassified scanner findings remaining: 0. Review findings closed: 10. Review findings remaining: 1.
None
None
Expand | File | Group | Subtype | Severity | Status |
|---|
No findings available
0 findings
Reference for diagnosing and fixing the most dangerous silent breaking change in React 18 for class-component codebases.
| Location of setState | React 17 | React 18 |
|---|---|---|
React event handler | Batched | Batched (same) |
setTimeout | Immediate re-render | Batched |
Promise .then() / .catch() | Immediate re-render | Batched |
async/await | Immediate re-render | Batched |
Native addEventListener callback | Immediate re-render | Batched |
Batched means: all setState calls within that execution context flush together in a single re-render at the end. No intermediate renders occur.
The React 18 column applies only when the app renders through the new React 18 root API. Apps still on the legacy render entry point keep React 17 behavior, so no batching regression applies there.
First check which root API the app uses. If the app is still on the legacy render entry point, the React 18 batching change does not apply.
Read every async method in the class components the user asked about. Ask: does code read this.state (or rely on an immediate re-render) right after calling setState in the same execution context outside a React event handler - after an await, inside setTimeout, inside a Promise .then() or .catch(), or in a native addEventListener callback?
Only modify components the user asked about or that show a confirmed regression. List other suspected sites for the user and get the user's confirmation before editing other files. Confirm behavior with tests before and after each change.
For the full pattern for each category, read:
references/batching-categories.md - Category A, B, C with full before/after codereferences/flushSync-guide.md - when to use flushSync, when NOT to, import syntaxIn short: Category A - use local variables instead of reading this.state right after setState; Category B - consolidate setState calls; Category C - wrap only the setState that must render before the async step in flushSync. The reference files are guidance only; keep the task within what the user asked for.
Use flushSync sparingly. It forces a synchronous re-render, bypassing React 18's concurrent scheduler. Overusing it negates the performance benefits of React 18.
Only use flushSync when:
In most cases, the fix is a minimal, local refactor of the affected method - restructuring the code to use local variables or the setState callback instead of reading this.state right after setState. Preserve existing behavior and keep unrelated code unchanged. Read references/batching-categories.md for the correct approach per category.
Keep assertions that guard user-visible behavior. Change a test to check only the final state when the intermediate render is genuinely not visible to the user (Category B). When the intermediate state must be visible, fix the component (Category C) and keep the assertion, using waitFor to wait for it. Report every assertion change to the user.
Get started with chainctlchainctl skill install public/github/react18-batching-patterns:latest
Bring Chainguard's hardening pipeline to your first-party skills — custom rulesets, automated remediation, full audit trail.
skills.cgr.dev/public/github.com/github/skills/react18-batching-patterns:latest
MIT
HARDENING.md
LICENSE
SKILL.md
references/batching-categories.md
references/flushSync-guide.md