React Rendering & Effects Guide
Authoritative reference for React escape hatches: refs, Effects, and custom Hooks. Based on official React documentation.
Quick Decision: Do You Need an Effect?
Ask: "Why does this code need to run?"
| Reason | Use |
|---|---|
| Props/state changed and you need a derived value | Calculate during render |
| User performed a specific interaction | Event handler |
| Component was displayed to the user | Effect |
| Synchronize with external system (network, DOM API, third-party widget) | Effect |
If there is no external system involved, you almost certainly don't need an Effect.
Core Concepts
Refs (useRef)
- Store values that persist across renders without triggering re-renders
- Access via
ref.current(mutable, synchronous) - Never read/write
ref.currentduring rendering (exception: lazy initif (!ref.current) ref.current = new Thing()) - Use for: timeout/interval IDs, DOM elements, non-rendering data
Refs vs State: State triggers re-renders; refs don't. State is immutable (use setter); refs are mutable. State has render snapshots; refs are always current.
DOM Refs
const inputRef = useRef(null);
// ...
<input ref={inputRef} />;
// After commit: inputRef.current is the DOM node
inputRef.current.focus();
- React sets
ref.currentduring commit phase, not during render - For lists of refs, use a ref callback with a
Map-- see refs-and-dom.md - Forward refs to child components via the
refprop; restrict withuseImperativeHandle - Use
flushSyncwhen you need state updates flushed to DOM before reading it
Effects (useEffect)
Effects synchronize components with external systems. They run after render (during commit).
useEffect(() => {
// setup: start synchronizing
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => {
// cleanup: stop synchronizing
connection.disconnect();
};
}, [roomId]); // re-run when roomId changes
Dependency array behavior:
useEffect(fn)-- runs after every renderuseEffect(fn, [])-- runs on mount onlyuseEffect(fn, [a, b])-- runs on mount + when a or b change (compared withObject.is)
You cannot choose your dependencies. They are determined by the reactive values read inside the Effect. The linter enforces this. Never suppress the linter.
Dev mode: Strict Mode runs Effects twice (mount -> unmount -> mount) to verify cleanup works.
For full Effect patterns, cleanup examples, and the lifecycle model, see effects-guide.md.
Critical: Anti-Patterns to Avoid
Full catalog of 12 anti-patterns with correct alternatives in anti-patterns.md.
Most common mistakes:
- Derived state in Effect -- Calculate during render instead
- Event logic in Effect -- Use event handlers instead
- Resetting state on prop change -- Use
keyprop instead - Chains of Effects -- Consolidate logic in event handler
- Object/function dependencies -- Move inside Effect or extract primitives
Dependency Management
When dependencies cause problems (too frequent, infinite loops), change the code, not the deps:
- Move non-reactive values outside the component
- Move dynamic objects/functions inside the Effect
- Extract primitives from object props via destructuring
- Use updater functions:
setCount(c => c + 1)instead of readingcount - Use
useEffectEventto read reactive values without re-triggering the Effect
See dependencies-and-events.md for full strategies.
Custom Hooks
- Must start with
use+ capital letter - Share stateful logic, not state itself -- each call creates independent state
- Re-run on every render like components; code must be pure
- Wrap received event handler callbacks in
useEffectEvent - Name after purpose (
useChatRoom), not lifecycle (useMount)
Anti-pattern: Don't create useMount, useEffectOnce, useUpdateEffect -- these hide dependency bugs and fight React's reactive model.
See custom-hooks.md for patterns and examples.
Reference Files
| File | Content |
|---|---|
| refs-and-dom.md | Refs, DOM manipulation, ref callbacks, forwarding, flushSync |
| effects-guide.md | Effect lifecycle, cleanup patterns, dev mode, data fetching |
| anti-patterns.md | 12 anti-patterns with correct alternatives and decision table |
| dependencies-and-events.md | Dependency strategies, useEffectEvent, object/function pitfalls |
| custom-hooks.md | Custom Hook patterns, naming, composing, useData, useSyncExternalStore |
Converted and distributed by TomeVault — claim your Tome and manage your conversions.