Context
Use this skill when a React page keeps re-rendering, refetching, or polling forever after mount or selection changes.
Patterns
- Trace the full effect chain:
useEffect-> callback/hook ->setState-> dependency churn. - If a callback is invoked by a mount effect, do not make that callback depend on maps, arrays, or objects that the callback also resets or replaces.
- Use refs to read the latest mutable collections inside async callbacks when you need current reconciliation data without changing callback identity.
- Prefer stable IDs/strings in polling and fetch effects over whole selected objects.
- Check retry toasts and polling callbacks for recursive calls that capture unstable objects.
Examples
src/aipolicyengine-ui/src/pages/Apis.tsx:loadInitialDataoriginally depended onoperationsByApiand also calledsetOperationsByApi({}), so the mount effect keyed toloadInitialDatare-fired continuously.- Fix pattern: mirror
operationsByApiintooperationsByApiRef.currentand read the ref inside the callback, leaving the callback deps stable.
Anti-Patterns
useEffect(() => { void loadInitialData() }, [loadInitialData])whenloadInitialDatadepends on state it also recreates.- Depending on inline objects/arrays or selected entity objects when only an ID is needed.
- Polling effects that re-arm on every render because status inputs are not stable.