React Hooks Cheatsheet
React Hooks Cheatsheet
A quick-reference guide for essential and advanced React Hooks (v18+), covering state, side effects, context, performance optimizations, DOM references, state reducers, and custom hooks.
Rules of Hooks
- Only Call Hooks at the Top Level: Do not call Hooks inside loops, conditions, or nested functions. Ensure they are always called in the exact same order on every render.
- Only Call Hooks from React Functions: Call Hooks only from React Functional Components or Custom Hooks, never from regular JavaScript functions.
State Hook: useState
useState allows functional components to store and manage local reactive state.
import { useState } from 'react';
function Counter() {
// Declaration: const [state, setter] = useState(initialValue);
const [count, setCount] = useState(0);
// 1. Basic Update
const increment = () => setCount(count + 1);
// 2. Functional Update (Always preferred when state depends on the previous state)
const decrement = () => setCount(prevCount => prevCount - 1);
// 3. Lazy Initial State (Used for expensive computations - runs only on initial render)
const [data, setData] = useState(() => {
return loadExpensiveDataFromStorage();
});
}
Side Effect Hook: useEffect
useEffect handles asynchronous operations or DOM operations (fetching, subscriptions, manual DOM changes).
import { useEffect, useState } from 'react';
function DataFetcher({ userId }) {
const [data, setData] = useState(null);
useEffect(() => {
let isMounted = true;
const controller = new AbortController();
// Effect logic (commonly API fetching)
async function fetchData() {
const res = await fetch(`https://api.com/users/${userId}`, { signal: controller.signal });
const json = await res.json();
if (isMounted) setData(json);
}
fetchData();
// Cleanup Function (Runs when component unmounts or dependencies change)
return () => {
isMounted = false;
controller.abort(); // Abort active network request to prevent memory leaks
};
}, [userId]); // Dependency Array: Effect re-runs ONLY when 'userId' changes
// Dependency Scenarios:
// useEffect(() => {}) // Runs on EVERY render
// useEffect(() => {}, []) // Runs ONCE on initial mount (equivalent to componentDidMount)
// useEffect(() => {}, [x]) // Runs ONCE on mount, and whenever 'x' changes
}
Context Hook: useContext
useContext consumes values from a React Context directly, avoiding the need for deep prop-drilling.
import { createContext, useContext, useState } from 'react';
// 1. Create Context
const ThemeContext = createContext(null);
// 2. Provider Component
export function ThemeProvider({ children }) {
const [theme, setTheme] = useState('dark');
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
{children}
</ThemeContext.Provider>
);
}
// 3. Consume Context (Inside child components)
function ThemeButton() {
const { theme, setTheme } = useContext(ThemeContext);
return (
<button onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}>
Current Theme: {theme}
</button>
);
}
Reference Hook: useRef
useRef stores mutable values that do not trigger component re-renders when updated, or acts as a direct reference to access physical DOM nodes.
import { useRef, useEffect } from 'react';
function FocusInput() {
// Scenario A: Accessing DOM elements
const inputRef = useRef(null);
useEffect(() => {
inputRef.current.focus(); // Focus input element physically on mount
}, []);
// Scenario B: Storing mutable values (e.g. interval timers) without triggering re-renders
const timerRef = useRef(null);
const startTimer = () => {
timerRef.current = setInterval(() => console.log("Tick"), 1000);
};
const stopTimer = () => {
clearInterval(timerRef.current); // Timer updated without causing a re-render
};
}
Memoization Hooks: useMemo & useCallback
These hooks cache (memoize) calculations or function references to avoid expensive re-computations or unnecessary re-renders of child components.
import { useMemo, useCallback, useState } from 'react';
function Search({ items }) {
const [query, setQuery] = useState('');
// 1. useMemo: Caches the computed VALUE of an expensive function
const filteredItems = useMemo(() => {
console.log("Expensive filter running...");
return items.filter(item => item.name.toLowerCase().includes(query.toLowerCase()));
}, [items, query]); // Only re-calculates when 'items' or 'query' changes
// 2. useCallback: Caches the FUNCTION REFERENCE itself
// Prevents child elements from triggering re-renders due to re-created function references on parent re-renders
const handleSelect = useCallback((itemId) => {
console.log("Selected item:", itemId);
}, []); // Empty dependencies: reference remains identical across all re-renders
}
State Reducer Hook: useReducer
useReducer is an alternative to useState preferred for complex state objects, nested state transitions, or when the next state depends heavily on the previous one.
import { useReducer } from 'react';
const initialState = { count: 0, loading: false };
// Reducer Function: purely functional state transitions
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { ...state, count: state.count + 1 };
case 'decrement':
return { ...state, count: state.count - 1 };
case 'setLoading':
return { ...state, loading: action.payload };
default:
throw new Error(`Unhandled action type: ${action.type}`);
}
}
// Consumer Component
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
<button onClick={() => dispatch({ type: 'decrement' })}>-</button>
</div>
);
}
Custom Hooks
Custom Hooks are custom JavaScript functions whose names start with use. They let you extract and reuse stateful logic between components.
import { useState, useEffect } from 'react';
// Custom Hook to track browser window dimensions
export function useWindowSize() {
const [size, setWindowSize] = useState({
width: typeof window !== 'undefined' ? window.innerWidth : 1200,
height: typeof window !== 'undefined' ? window.innerHeight : 800,
});
useEffect(() => {
const handleResize = () => {
setWindowSize({ width: window.innerWidth, height: window.innerHeight });
};
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize); // Cleanup
}, []);
return size;
}
// Consumer component
function ResponsiveComponent() {
const { width } = useWindowSize(); // Consuming stateful window size logic instantly
return <p>Window Width is {width}px</p>;
}