Files
awesome-copilot/plugins/react18-upgrade/skills/react18-batching-patterns/references/flushSync-guide.md
2026-04-09 06:26:21 +00:00

87 lines
2.4 KiB
Markdown

# flushSync Guide
## Import
```jsx
import { flushSync } from 'react-dom';
// NOT from 'react' - it lives in react-dom
```
If the file already imports from `react-dom`:
```jsx
import ReactDOM from 'react-dom';
// Add named import:
import ReactDOM, { flushSync } from 'react-dom';
```
## Syntax
```jsx
flushSync(() => {
this.setState({ ... });
});
// After this line, the re-render has completed synchronously
```
Multiple setState calls inside one flushSync batch together into ONE synchronous render:
```jsx
flushSync(() => {
this.setState({ step: 'loading' });
this.setState({ progress: 0 });
// These batch together → one render
});
```
## When to Use
✅ Use when the user must see a specific UI state BEFORE an async operation starts:
```jsx
flushSync(() => this.setState({ loading: true }));
await expensiveAsyncOperation();
```
✅ Use in multi-step progress flows where each step must visually complete before the next:
```jsx
flushSync(() => this.setState({ status: 'validating' }));
await validate();
flushSync(() => this.setState({ status: 'processing' }));
await process();
```
✅ Use in tests that must assert an intermediate UI state synchronously (avoid when possible - prefer `waitFor`).
## When NOT to Use
❌ Don't use it to "fix" a reading-this.state-after-await bug - that's Category A (refactor instead):
```jsx
// WRONG - flushSync doesn't fix this
flushSync(() => this.setState({ loading: true }));
const data = await fetchData();
if (this.state.loading) { ... } // still a race condition
```
❌ Don't use it for every setState to "be safe" - it defeats React 18 concurrent rendering:
```jsx
// WRONG - excessive flushSync
async handleClick() {
flushSync(() => this.setState({ clicked: true })); // unnecessary
flushSync(() => this.setState({ processing: true })); // unnecessary
const result = await doWork();
flushSync(() => this.setState({ result, done: true })); // unnecessary
}
```
❌ Don't use it inside a `useEffect` or `componentDidMount` to trigger immediate state - it causes nested render cycles.
## Performance Note
`flushSync` forces a synchronous render, which blocks the browser thread until the render completes. On slow devices or complex component trees, multiple `flushSync` calls in an async method will cause visible jank. Use sparingly.
If you find yourself adding more than 2 `flushSync` calls to a single method, reconsider whether the component's state model needs redesign.