Add Copilot review instructions for in-render React state updates#92410
Add Copilot review instructions for in-render React state updates#92410MelvinBot wants to merge 2 commits into
Conversation
Tell GitHub Copilot code review not to flag guarded state setter calls made during render. Adjusting state during render is a React-recommended, React-Compiler-compatible pattern, so Copilot should not suggest moving these updates into effects. Co-authored-by: Vit Horacek <mountiny@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Adds repository-level GitHub Copilot review instructions to prevent false-positive “state update during render” findings for guarded, converging render-phase local state updates in React components/hooks used in this codebase.
Changes:
- Introduces
.github/copilot-instructions.mdto customize Copilot’s PR review behavior for this repo. - Documents that guarded render-phase local state updates are a valid React pattern and should not be blanket-suggested to move into effects.
- Links to React docs and the repo’s React Compiler guidance for supporting context.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| Do not flag a component or hook that calls a state setter (for example `setState`) directly during render as a bug, an anti-pattern, a "state update during render" problem, a source of "render loops", or as something that "defeats React Compiler compliance". Adjusting state during render — including storing information from previous renders and resetting state when a prop or input changes — is an officially recommended React pattern. | ||
|
|
||
| This is correct as long as the update is guarded by a condition that eventually stops being true (so it converges instead of looping). Specifically: | ||
|
|
||
| - React handles a synchronous setter call made during render by immediately re-rendering the same component with the updated state, before committing to the screen. There is no extra browser paint, no visible flash, and no infinite loop when the update is conditional. |
There was a problem hiding this comment.
I think we should provide an example here and link to the official docs
There was a problem hiding this comment.
I think we should provide an example here and link to the official docs
@MelvinBot Can you implement these suggestions?
There was a problem hiding this comment.
Done in c557264. I implemented both suggestions from this thread:
Example + official docs link (adhorodyski): added the officially recommended React example for adjusting state when a prop changes, with the inline doc link right next to it. The example highlights the prevCount !== count guard that makes the update converge.
Tightened scope (Copilot): the exception now explicitly applies only to a component updating its own local state with a converging guard, and I added a "continue to flag" list so cross-component/context setter calls, external writes/dispatches during render, and non-converging (looping) updates are still flagged.
Add the officially recommended React example for adjusting state when a prop changes (with the converging guard), link the official docs inline, and narrow the exception to guarded self-state updates so cross-component or external writes during render and non-converging updates are still flagged. Co-authored-by: Vit Horacek <mountiny@users.noreply.github.com>
Explanation of Change
Adds a GitHub Copilot custom instructions file (
.github/copilot-instructions.md) so Copilot's automated PR reviews stop flagging a valid React pattern.In #91179, Copilot's review repeatedly flagged guarded state setter calls made during render as a bug / "state update during render" problem / "render loop" risk / something that "defeats React Compiler compliance", and recommended moving them into effects:
These are false positives. Adjusting state during render (storing info from previous renders, resetting state when an input changes) is an officially recommended React pattern: when the update is guarded by a condition that eventually stops being true, React re-renders the same component synchronously before committing to the screen — no extra paint, no flash, no infinite loop. It is also React-Compiler-compatible, and React Compiler runs in this repo's build pipeline. Moving such updates into
useEffect/useLayoutEffectis usually worse (extra render+commit cycle, possible flash of stale UI).The new instructions tell Copilot not to flag these guarded in-render state updates and not to suggest moving them into effects.
Fixed Issues
$
PROPOSAL:
Tests
// TODO: The human co-author must fill out the tests you ran before marking this PR as "ready for review".
// Please describe what tests you performed that validate your change worked.
Offline tests
N/A — this PR only adds a Markdown configuration file consumed by GitHub Copilot; there is no runtime/app behavior change.
QA Steps
// TODO: The human co-author must fill out the QA tests you ran before marking this PR as "ready for review".
// Please describe what QA needs to do to validate your changes and what areas they need to test for regressions.