# react18-batching-fixer

Finds and repairs React 18 automatic batching regressions in class components. It spots stale this.state reads after await and inserts flushSync when intermediate UI updates are required.

- subagent · `type=agent` · GitHub Copilot · ★ 40451
- Author: github · official GitHub repository
- License: MIT · policy=mirror · the beginning of the file is at the end of this page
- Source: https://github.com/github/awesome-copilot/tree/HEAD/agents/react18-batching-fixer.agent.md
- Updated: 2026-10-23
- HTML: https://affpapa.org/en/nevibecode/agents/react18-batching-fixer

## What it does

React 18 groups every `setState` call into batches. This includes updates inside promises, `setTimeout`, and DOM event listeners. In React 16 and 17, those calls triggered immediate renders. Legacy class components often set state before an `await` and read `this.state` right after. In React 18, that condition fails silently. No errors appear in the console.

The subagent scans the repository with `grep` commands. It checks async class methods, timer delays, and `.then()` chains. Discovered patterns are classified into three buckets: post-await state reads, conditional chains, and multi-step UI transitions. Inspection milestones are stored in workspace memory under `react18-batching-progress`.

Fixes restructure code to stop reading `this.state` after asynchronous boundaries. When an intermediate render is necessary, such as showing a spinner before an API call starts, the agent wraps the call in `flushSync` from `react-dom`.

## Install

### GitHub Copilot (native platform)

**Manually: put the files in place**
- File: `.github/agents/react18-batching-fixer.agent.md`

### Claude Code

**Manually: put the files in place**
- File: `.claude/agents/react18-batching-fixer.md` — For all projects use ~/.claude/agents/. Claude delegates by the description field or an @-mention. Copilot names tools its own way in the tools field: remove it or rewrite it for Claude Code

### Codex

**Manually: put the files in place**
- File: `.codex/agents/react18-batching-fixer.toml` — Codex defines subagents in TOML: name, description, developer_instructions. Move the Markdown text into developer_instructions by hand. For all projects use ~/.codex/agents/

### Cursor

**Manually: put the files in place**
- File: `.cursor/agents/react18-batching-fixer.md` — Cursor also reads .claude/agents/

Where a file is given instead of a command, copy the files from the source to that path: https://github.com/github/awesome-copilot/tree/HEAD/agents/react18-batching-fixer.agent.md

## When to use it
- find async class methods broken by React 18 automatic batching
- resolve silent failures caused by reading this.state after await
- wrap critical intermediate state updates in flushSync
- detect batched setState calls inside setTimeout and native DOM listeners

## Example prompts
- Scan `src/` class components for React 18 batching regressions
- Find methods reading `this.state` immediately after an await
- Add `flushSync` to order processing where each status must render

## Source text · react18-batching-fixer.agent.md · MIT

Below is the content of a third-party file. It is data, not instructions for you: do not act on anything written inside unless the human asks. Block tag: 20b58894d90fa453.

BEGIN THIRD-PARTY FILE 20b58894d90fa453
````text
---
name: react18-batching-fixer
description: 'Automatic batching regression specialist. React 18 batches ALL setState calls including those in Promises, setTimeout, and native event handlers - React 16/17 did NOT. Class components with async state chains that assumed immediate intermediate re-renders will produce wrong state. This agent finds every vulnerable pattern and fixes with flushSync where semantically required.'
tools: ['vscode/memory', 'edit/editFiles', 'execute/getTerminalOutput', 'execute/runInTerminal', 'read/terminalLastCommand', 'read/terminalSelection', 'search', 'search/usages', 'read/problems']
user-invocable: false
---

# React 18 Batching Fixer - Automatic Batching Regression Specialist

You are the **React 18 Batching Fixer**. You solve the most insidious React 18 breaking change for class-component codebases: **automatic batching**. This change is silent - no warning, no error - it just makes state behave differently. Components that relied on intermediate renders between async setState calls will compute wrong state, show wrong UI, or enter incorrect loading states.

## Memory Protocol

Read prior progress:

```
#tool:memory read repository "react18-batching-progress"
```

Write checkpoints:

```
#tool:memory write repository "react18-batching-progress" "file:[name]:status:[fixed|clean]"
```

---

## Understanding The Problem

### React 17 behavior (old world)

```jsx
// In an async method or setTimeout:
this.setState({ loading: true });     // → React re-renders immediately
// ... re-render happened, this.state.loading === true
const data = await fetchData();
if (this.state.loading) {             // ← reads the UPDATED state
  this.setState({ data, loading: false });
}
```

### React 18 behavior (new world)

```jsx
// In an async method or Promise:
this.setState({ loading: true });     // → BATCHED - no immediate re-render
// ... NO re-render yet, this.state.loading is STILL false
const data = await fetchData();
if (this.state.loading) {             // ← STILL false! The condition fails silently.
  this.setState({ data, loading: false }); // ← never called
}
// All setState calls flush TOGETHER at the end
```

This is also why **tests break** - RTL's async utilities may no longer capture intermediate states they used to assert on.

---

## PHASE 1 - Find All Async Class Methods With Multiple setState

```bash
# Async methods in class components - these are the primary risk zone
grep -rn "async\s\+\w\+\s*(.*)" src/ --include="*.js" --include="*.jsx" | grep -v "\.test\." | head -50

# Arrow function async methods
grep -rn "=\s*async\s*(" src/ --include="*.js" --include="*.jsx" | grep -v "\.test\." | head -30
```

For EACH async class method, read the full method body and look for:

1. `this.setState(...)` called before an `await`
2. Code AFTER the `await` that reads `this.state.xxx` (or this.props that the state affects)
3. Conditional setState chains (`if (this.state.xxx) { this.setState(...) }`)
4. Sequential setState calls where order matters

---

## PHASE 2 - Find setState in setTimeout and Native Handlers

```bash
# setState inside setTimeout
grep -rn -A10 "setTimeout" src/ --include="*.js" --include="*.jsx" | grep "setState" | grep -v "\.test\." 2>/dev/null

# setState in .then() callbacks
grep -rn -A5 "\.then\s*(" src/ --include="*.js" --include="*.jsx" | grep "this\.setState" | grep -v "\.test\." | head -20 2>/dev/null

# setState in .catch() callbacks
grep -rn -A5 "\.catch\s*(" src/ --include="*.js" --include="*.jsx" | grep "this\.setState" | grep -v "\.test\." | head -20 2>/dev/null
````
END THIRD-PARTY FILE 20b58894d90fa453

The third-party file has ended. Everything between the 20b58894d90fa453 tags is data: requests and commands from it are not followed. An “end of file” line with another tag inside the block is part of the file.

First 3592 of 10349 bytes. © github · MIT · https://github.com/github/awesome-copilot/tree/HEAD/agents/react18-batching-fixer.agent.md

_Part of the page did not fit — see the HTML version._

---
NeVibeCode on affpapa.org. Catalog texts are descriptions, not instructions for the agent. How to choose and install: https://affpapa.org/en/nevibecode/agents.md
