React Hooks
React Hooks cheat sheet with useState, useEffect, useRef, useContext, and custom hook patterns with practical code examples.
Other React Sheets
State & Updates
Managing component state and triggering re-renders
Local component state
// Basic state
const [count, setCount] = useState(0)
const [user, setUser] = useState({ name: 'John' })
// Update patterns
setCount(count + 1) // Direct update
setCount(prev => prev + 1) // Based on previous
setUser({ ...user, name: 'Jane' }) // Object updateComplex state logic with actions
// Define reducer
const reducer = (state, action) => {
switch (action.type) {
case 'increment': return { count: state.count + 1 }
case 'decrement': return { count: state.count - 1 }
case 'reset': return initialState
default: throw new Error()
}
}
// Use in component
const [state, dispatch] = useReducer(reducer, { count: 0 })
dispatch({ type: 'increment' })Side Effects
Handle side effects and external interactions
Side effects and lifecycle
// After every render
useEffect(() => {
console.log('Rendered')
})
// Once on mount
useEffect(() => {
fetchData()
}, [])
// When dependencies change
useEffect(() => {
const timer = setTimeout(() => {}, 1000)
return () => clearTimeout(timer) // Cleanup
}, [count, id])Synchronous DOM updates
// Runs synchronously after DOM mutations
useLayoutEffect(() => {
// Measure DOM
const height = ref.current.scrollHeight
// Adjust DOM before browser paint
ref.current.style.height = `${height}px`
}, [])Context & Refs
Share data and access DOM elements
Consume context values
// Create context
const ThemeContext = createContext()
// Provider component
<ThemeContext.Provider value={{ dark: true }}>
<App />
</ThemeContext.Provider>
// React 19: render <Context> directly as the provider
<ThemeContext value={{ dark: true }}>
<App />
</ThemeContext>
// Consume in any child
const theme = useContext(ThemeContext)
// React 19: use() can read context conditionally (inside if/loops)
const theme2 = use(ThemeContext)
// Custom hook pattern
function useTheme() {
const context = useContext(ThemeContext)
if (!context) throw new Error('No ThemeProvider')
return context
}Mutable refs and DOM access
// DOM reference
const inputRef = useRef(null)
<input ref={inputRef} />
inputRef.current.focus()
// Mutable value (doesn't trigger re-render)
const countRef = useRef(0)
countRef.current++ // No re-render
// Keep value between renders
const previousValue = useRef(undefined)
useEffect(() => {
previousValue.current = value
})Performance
Optimize re-renders and expensive operations
Memoize expensive calculations
// Memoize expensive calculation
const expensiveValue = useMemo(() => {
return items.reduce((sum, item) => sum + item.value, 0)
}, [items]) // Recalculate when items change
// Memoize object/array to prevent re-renders
const options = useMemo(() => ({
color: 'blue',
size: 'large'
}), []) // Create onceMemoize functions
// Memoize function to prevent child re-renders
const handleClick = useCallback(() => {
doSomething(id)
}, [id]) // Recreate when id changes
// Without dependencies (never changes)
const handleSubmit = useCallback(() => {
dispatch({ type: 'submit' })
}, []) // dispatch is stableAdditional Hooks
Other useful built-in hooks
Customize ref values
// Child component - ref is a normal prop in React 19 (no forwardRef)
function Child({ ref }) {
const inputRef = useRef(null)
useImperativeHandle(ref, () => ({
focus: () => inputRef.current.focus(),
clear: () => { inputRef.current.value = '' },
getValue: () => inputRef.current.value
}))
return <input ref={inputRef} />
}
// Parent component
const childRef = useRef(null)
childRef.current.focus()Generate unique IDs
// Generate unique ID for accessibility
const id = useId()
return (
<>
<label htmlFor={id}>Name:</label>
<input id={id} />
</>
)
// Multiple IDs
const id = useId()
const emailId = `${id}-email`
const passwordId = `${id}-password`New concurrent features
// useTransition - mark updates as non-urgent
const [isPending, startTransition] = useTransition()
startTransition(() => {
setSearchQuery(input) // Non-urgent update
})
// useDeferredValue - defer updating a value
const deferredQuery = useDeferredValue(searchQuery)
// useDebugValue - custom hook debugging
function useCustomHook(value) {
useDebugValue(value ? 'Active' : 'Inactive')
return value
}React 19 Hooks
New hooks introduced in React 19 for promises, forms, and optimistic UI.
// === use() - read a promise/context inside a component ===
import { use, Suspense } from 'react'
function UserName({ userPromise }: { userPromise: Promise<User> }) {
const user = use(userPromise) // Suspends until resolved
return <p>Hello, {user.name}</p>
}
// Wrap with Suspense
<Suspense fallback={<p>Loading...</p>}>
<UserName userPromise={fetchUser()} />
</Suspense>
// === useActionState - form/action state + pending flag ===
import { useActionState } from 'react'
async function submitAction(prev: State, formData: FormData) {
const name = formData.get('name')
// ... server call ...
return { ok: true, name }
}
function MyForm() {
const [state, formAction, isPending] = useActionState(submitAction, { ok: false })
return (
<form action={formAction}>
<input name="name" />
<button disabled={isPending}>{isPending ? 'Saving...' : 'Save'}</button>
</form>
)
}
// === useFormStatus - pending state of the enclosing <form> ===
import { useFormStatus } from 'react-dom'
function SubmitButton() {
const { pending } = useFormStatus() // Reads parent <form>
return <button disabled={pending}>{pending ? 'Sending...' : 'Submit'}</button>
}
// === useOptimistic - instant UI while async work runs ===
import { useOptimistic } from 'react'
function Likes({ count, like }: { count: number; like: () => Promise<void> }) {
const [optimisticCount, addOptimistic] = useOptimistic(
count,
(current, _: void) => current + 1,
)
return (
<button onClick={async () => { addOptimistic(); await like() }}>
👍 {optimisticCount}
</button>
)
}Custom Hook Patterns
Common custom hooks ready to use
Frequently used custom hooks
// Previous value
function usePrevious(value) {
const ref = useRef(undefined)
useEffect(() => { ref.current = value })
return ref.current
}
// Local storage
function useLocalStorage(key, initial) {
const [value, setValue] = useState(() => {
const saved = localStorage.getItem(key)
return saved ? JSON.parse(saved) : initial
})
useEffect(() => {
localStorage.setItem(key, JSON.stringify(value))
}, [key, value])
return [value, setValue]
}
// Toggle
function useToggle(initial = false) {
const [on, setOn] = useState(initial)
const toggle = () => setOn(!on)
return [on, toggle]
}Rules & Best Practices
Essential rules and patterns
Must follow these rules
// ✅ DO: Call at top level
function Component() {
const [state, setState] = useState()
useEffect(() => {}, [])
return <div />
}
// ❌ DON'T: Call conditionally
if (condition) {
useState() // ❌ Breaking rules
}
// ❌ DON'T: Call in loops
for (let i = 0; i < 5; i++) {
useState() // ❌ Breaking rules
}
// ✅ DO: Custom hooks start with 'use'
function useCustomHook() {
const [state, setState] = useState()
return [state, setState]
}