mirror of
https://github.com/github/awesome-copilot.git
synced 2026-04-11 10:45:56 +00:00
87 lines
2.4 KiB
Markdown
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.
|