| name | react-principles |
| description | React best practices for component design, state management, and Effect discipline. Use when writing, reviewing, or refactoring React components, custom hooks, or any .ts/.tsx/.js/.jsx file that uses React. |
React Development Principles
Component Model
- Components are pure functions:
(props, state) โ JSX. Render must be side-effect-free.
- One-way data flow: parent โ child via props, child โ parent via callbacks.
- Never mutate state or props in-place.
Thinking in React: Design Process
- Hierarchy โ decompose by SRP; one component, one concern. Map data model shape to component tree.
- Static first โ props only, no state, until the skeleton renders correctly.
- Minimal state โ DRY: if it's derivable, it's not state.
- Ownership โ hoist state to the closest common ancestor of all consumers.
- Inverse flow โ pass setters/handlers down; children call them on interaction.
What is NOT state
- Computable from existing state or props โ derive during render
- Passed in as a prop
- Unchanged over time
Prefer storing IDs/primitives; derive full objects at render time.
Effect Discipline
useEffect is for synchronizing with external systems only โ DOM APIs, network connections, third-party widgets, browser subscriptions. It is not a lifecycle hook and not a data-transformation pipeline.
Deciding when to write an Effect
Why does this code run?
- Because the user did something โ event handler
- Because the component must stay in sync with something external โ Effect
Unnecessary Effects โ replace with:
| Antipattern | Correct approach |
|---|
Derived state via useEffect + useState | Compute during render; useMemo if expensive |
| State reset on prop change via Effect | key prop |
| Partial state adjustment on prop change | Store primitive ID; derive object during render |
| Shared event logic routed through state+Effect | Extract a plain function; call from event handlers |
| User-initiated POST in Effect | Move directly into the event handler |
| Effect chains (A โ Effect โ B โ Effect โ C) | Compute all next state in one event handler |
| Parent notification via Effect | Call both setters in the same event handler; or lift state |
| Pushing data up to parent via Effect | Parent fetches; passes data down as prop |
| One-time app init in component Effect | Module-level or didInit guard outside component |
| External store subscription via Effect | useSyncExternalStore |
| Data fetch without cleanup | ignore flag cleanup; prefer framework data-fetching |
Effect rules
- Every reactive value the Effect reads must be in its dependency array.
- Never suppress
react-hooks/exhaustive-deps โ fix the code.
- Every resource acquired in setup must be released in cleanup.
- One Effect = one synchronization concern. Split unrelated concerns.
- Think start sync / stop sync, not mount / unmount.
Key Heuristics
- If it's computable at render time โ compute it. No state, no Effect.
- If it runs because the user did X โ it's an event handler.
- If it runs because the component is visible โ it's an Effect.
- If two state variables drift out of sync โ lift state or derive one from the other.
- If the dep array is long โ the Effect does too much; split it.
- If you want to suppress the deps linter โ redesign instead.
Supporting Reference Files
Load these files when deeper guidance is needed for the specific topic at hand: