Skip to content

React Quick Reference

A quick lookup for React concepts and patterns grouped by category. All examples use function components and modern hooks (React 18+).

RuleCorrectWrong
Single root element<><h1/><p/></><h1/><p/>
Self-close void elements<input /><input>
className not classclassName="card"class="card"
htmlFor not forhtmlFor="id"for="id"
camelCase eventsonClickonclick
Expressions in {}{count + 1}
Inline styles as objectstyle={{ color: 'red' }}style="color:red"

const [value, setValue] = useState(initialValue);
// Set a new value
setValue('new');
// Functional update — use when new state depends on previous
setValue(prev => prev + 1);
// Lazy initializer — only runs once
const [data, setData] = useState(() => JSON.parse(localStorage.getItem('key') ?? 'null'));

Never mutate state directly. Always pass a new value to the setter.

// Run once on mount
useEffect(() => {
fetchData();
}, []);
// Run when dependency changes
useEffect(() => {
document.title = `Page — ${name}`;
}, [name]);
// With cleanup (timers, listeners, subscriptions)
useEffect(() => {
const id = setInterval(tick, 1000);
return () => clearInterval(id);
}, []);
// Async inside useEffect
useEffect(() => {
async function load() {
const res = await fetch('/api/data');
const json = await res.json();
setData(json);
}
load();
}, []);

Dependency array rules:

  • Empty [] — runs once after mount
  • [a, b] — runs when a or b changes
  • No array — runs after every render (rarely correct)
  • Include every reactive value the effect reads
// DOM reference
const inputRef = useRef(null);
inputRef.current.focus();
<input ref={inputRef} />
// Mutable value that does NOT trigger re-render
const countRef = useRef(0);
countRef.current++;
// Cache an expensive computation
const total = useMemo(
() => items.reduce((sum, i) => sum + i.price, 0),
[items] // only recompute when items changes
);

Use when the computation is measurably slow or when the result is passed to a React.memo child.

// Cache a function reference
const handleDelete = useCallback((id) => {
setItems(prev => prev.filter(i => i.id !== id));
}, []); // stable reference — no dependencies needed with functional setter

Use when the function is passed to a React.memo component or listed in a useEffect dependency array.

const value = useContext(MyContext);

See the Context section below for the full pattern.

function reducer(state, action) {
switch (action.type) {
case 'increment': return { count: state.count + 1 };
case 'reset': return { count: 0 };
default: return state;
}
}
const [state, dispatch] = useReducer(reducer, { count: 0 });
dispatch({ type: 'increment' });
dispatch({ type: 'reset' });

Prefer useReducer over multiple useState calls when state transitions are complex or interdependent.


export default function Card({ title, children }) {
return (
<div className="card">
<h2>{title}</h2>
{children}
</div>
);
}
// Passing
<Card title="Housing" amount={1200} isActive />
// Receiving (destructure in parameter)
function Card({ title, amount, isActive = false }) { ... }
// Spreading
function Input({ label, ...inputProps }) {
return <><label>{label}</label><input {...inputProps} /></>;
}
// Ternary — choose between two elements
{isLoggedIn ? <Dashboard /> : <Login />}
// Logical AND — render or nothing (avoid with numbers — use count > 0 &&)
{hasItems && <ItemList items={items} />}
// Early return
if (isLoading) return <Spinner />;
return <Content />;
{items.map(item => (
<Card key={item.id} item={item} /> // key on outermost element from map
))}
  • Keys must be unique among siblings
  • Keys must be stable — use IDs, not array indexes
  • Keys are not accessible as props inside the component
function Wrapper({ children }) {
return <div className="wrapper">{children}</div>;
}
<Wrapper>
<p>Any content here becomes children.</p>
</Wrapper>

// Click
<button onClick={handleClick}>Click</button>
<button onClick={() => handleClick(item.id)}>Delete</button>
// Form submit
<form onSubmit={e => { e.preventDefault(); handleSubmit(); }}>
// Input change
<input onChange={e => setValue(e.target.value)} value={value} />
// Checkbox
<input type="checkbox" checked={agreed} onChange={e => setAgreed(e.target.checked)} />
// Key press
<input onKeyDown={e => { if (e.key === 'Enter') save(); }} />
Common events
onClickMouse click
onChangeInput/select value changed
onSubmitForm submitted
onKeyDown / onKeyUpKey pressed / released
onFocus / onBlurElement gains / loses focus
onMouseEnter / onMouseLeaveHover enter / leave

// Text / number input
const [name, setName] = useState('');
<input value={name} onChange={e => setName(e.target.value)} />
// Select
const [category, setCategory] = useState('');
<select value={category} onChange={e => setCategory(e.target.value)}>
<option value="">Select…</option>
{options.map(o => <option key={o.id} value={o.id}>{o.label}</option>)}
</select>
// Checkbox
const [agreed, setAgreed] = useState(false);
<input type="checkbox" checked={agreed} onChange={e => setAgreed(e.target.checked)} />

setItems(prev => [...prev, newItem]);
setItems(prev => prev.filter(i => i.id !== id));
setItems(prev => prev.map(i => i.id === id ? { ...i, ...updates } : i));
setState(prev => ({ ...prev, name: 'new name' }));

Never mutate: state.name = 'new' or items.push(x) will not trigger re-renders.


import { memo } from 'react';
const Card = memo(function Card({ item }) {
return <div>{item.name}</div>;
});

Skips re-render if all props are unchanged (by reference). Pair with useCallback for function props and useMemo for object/array props to prevent reference changes.

ToolWhat it cachesUse when
React.memoComponent renderChild receives stable props but parent re-renders often
useMemoA computed valueExpensive calculation; value passed to a React.memo child
useCallbackA function referenceFunction passed to a React.memo child or used in useEffect deps

Measure before applying. These tools have overhead — don’t add them by default.


// 1. Create
const ThemeContext = createContext(null);
// 2. Provider
function ThemeProvider({ children }) {
const [theme, setTheme] = useState('dark');
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
{children}
</ThemeContext.Provider>
);
}
// 3. Custom hook (recommended pattern)
function useTheme() {
const ctx = useContext(ThemeContext);
if (!ctx) throw new Error('useTheme must be used inside ThemeProvider');
return ctx;
}
// 4. Consume
function Button() {
const { theme } = useTheme();
return <button className={theme}>Click</button>;
}
// 5. Wire up
<ThemeProvider>
<App />
</ThemeProvider>

// Convention: function name starts with "use"
function useLocalStorage(key, initialValue) {
const [value, setValue] = useState(() => {
try {
const item = localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch {
return initialValue;
}
});
const set = (next) => {
const v = next instanceof Function ? next(value) : next;
setValue(v);
localStorage.setItem(key, JSON.stringify(v));
};
return [value, set];
}
// Usage — drop-in replacement for useState
const [name, setName] = useLocalStorage('name', '');

Rules that apply to custom hooks:

  • Must start with use
  • Can call other hooks
  • Cannot be called conditionally or inside loops

const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const controller = new AbortController();
async function fetch() {
try {
const res = await fetch('/api/data', { signal: controller.signal });
if (!res.ok) throw new Error('Request failed');
setData(await res.json());
} catch (err) {
if (err.name !== 'AbortError') setError(err.message);
} finally {
setLoading(false);
}
}
fetch();
return () => controller.abort();
}, []);
// Don't do this:
const [total, setTotal] = useState(0);
useEffect(() => setTotal(items.reduce(...)), [items]);
// Do this — compute during render:
const total = items.reduce((sum, i) => sum + i.price, 0);

When two siblings need shared data, move state to their nearest common ancestor and pass data + callbacks as props.

function Parent() {
const [items, setItems] = useState([]);
function addItem(item) {
setItems(prev => [...prev, item]);
}
return (
<>
<Form onAdd={addItem} />
<List items={items} />
</>
);
}