React Cheatsheet
State and useState
Use this React reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.
useState — Basic Usage
import { useState } from 'react'; // Signature: const [state, setState] = useState(initialValue) const [count, setCount] = useState(0); const [name, setName] = useState(''); const [flag, setFlag] = useState(false); const [items, setItems] = useState([]); const [user, setUser] = useState(null); const [data, setData] = useState({ x: 0, y: 0 });
Reading and Setting State
function Counter() { const [count, setCount] = useState(0); // Direct value update const reset = () => setCount(0); // Functional update — use when new state depends on previous state const increment = () => setCount(prev => prev + 1); const decrement = () => setCount(prev => prev - 1); const double = () => setCount(prev => prev * 2); return ( <div> <p>{count}</p> <button onClick={increment}>+</button> <button onClick={decrement}>-</button> <button onClick={reset}>Reset</button> </div> ); }
Always use the functional form
prev => ...when the new value depends on the old value. DirectsetCount(count + 1)can use a stale value if called multiple times in one event.
Lazy Initial State
// If computing the initial value is expensive, pass a function — runs once only const [data, setData] = useState(() => { return JSON.parse(localStorage.getItem('saved') ?? '[]'); });
Updating Objects
React state updates are shallow — always create a new object.
const [user, setUser] = useState({ name: 'Alice', age: 30, role: 'admin' }); // Spread the old object, override changed fields const updateName = (name) => setUser(prev => ({ ...prev, name })); const updateAge = (age) => setUser(prev => ({ ...prev, age })); // Nested objects — must spread at every level const [form, setForm] = useState({ address: { city: '', zip: '' } }); const updateCity = (city) => setForm(prev => ({ ...prev, address: { ...prev.address, city } }));
Updating Arrays
const [items, setItems] = useState([{ id: 1, text: 'Buy milk' }]); // Add const addItem = (item) => setItems(prev => [...prev, item]); const prepend = (item) => setItems(prev => [item, ...prev]); // Remove const remove = (id) => setItems(prev => prev.filter(i => i.id !== id)); // Replace one item const update = (id, changes) => setItems(prev => prev.map(i => i.id === id ? { ...i, ...changes } : i)); // Sort (must copy first — sort() mutates) const sortByName = () => setItems(prev => [...prev].sort((a, b) => a.text.localeCompare(b.text))); // Insert at index const insertAt = (index, item) => setItems(prev => [...prev.slice(0, index), item, ...prev.slice(index)]); // Remove at index const removeAt = (index) => setItems(prev => prev.filter((_, i) => i !== index));
Multiple State Variables vs One Object
// Multiple — simpler, preferred when fields are unrelated const [firstName, setFirstName] = useState(''); const [lastName, setLastName] = useState(''); const [email, setEmail] = useState(''); // One object — useful when fields always update together const [coords, setCoords] = useState({ x: 0, y: 0 }); const move = (x, y) => setCoords({ x, y }); // both always update together
State is Asynchronous / Batched
// setState does NOT immediately update the variable — reads are stale in the // same synchronous block function Example() { const [count, setCount] = useState(0); const handleClick = () => { setCount(count + 1); // schedules update console.log(count); // still the OLD value — 0 }; // Fix: use functional form or read in the next render const handleClickFixed = () => { setCount(prev => { console.log(prev); // correct return prev + 1; }); }; } // React 18+ batches ALL setState calls (including in async/setTimeout/events) // React 17 and below only batched inside React event handlers
Resetting State — Key Trick
Changing the key prop unmounts + remounts the component, resetting all its state.
function App() { const [userId, setUserId] = useState(1); // ProfileForm re-mounts fresh every time userId changes return <ProfileForm key={userId} userId={userId} />; }
Derived State
Compute values from existing state rather than duplicating them.
// BAD — redundant state that can fall out of sync const [items, setItems] = useState([]); const [total, setTotal] = useState(0); // derived, don't store separately // GOOD — compute during render const [items, setItems] = useState([]); const total = items.reduce((sum, i) => sum + i.price, 0); // derived // If computation is expensive, memoize with useMemo const sortedItems = useMemo(() => [...items].sort(...), [items]);
Lifting State Up
Move state to the closest common ancestor when two sibling components need to share it.
// Parent owns the shared state function Parent() { const [value, setValue] = useState(''); return ( <> <Input value={value} onChange={setValue} /> <Preview value={value} /> </> ); } function Input({ value, onChange }) { return <input value={value} onChange={e => onChange(e.target.value)} />; } function Preview({ value }) { return <p>Preview: {value}</p>; }
Common Patterns and Gotchas
// Toggle boolean const [open, setOpen] = useState(false); const toggle = () => setOpen(prev => !prev); // Increment / counter pattern const [n, setN] = useState(0); const inc = () => setN(n => n + 1); // Track input value const [text, setText] = useState(''); <input value={text} onChange={e => setText(e.target.value)} /> // GOTCHA: Objects/arrays must be NEW references for React to detect change const [arr, setArr] = useState([1, 2, 3]); arr.push(4); // BAD — same reference, no re-render setArr([...arr, 4]); // GOOD // GOTCHA: State initialized from props is NOT automatically synced function Child({ initialCount }) { const [count, setCount] = useState(initialCount); // ignores later prop changes } // Fix: either derive from props (no state) or use useEffect to sync (anti-pattern); // better yet, lift state to parent.