{ ILoveJS }

React Hooks Cheatsheet

react

Quick reference for all React hooks with syntax and examples.

8 sections · 37 items

State Management Hooks

useState
const [state, setState] = useState<T>(initialState: T | (() => T)): [T, Dispatch<SetStateAction<T>>]

Declares a state variable that triggers re-renders when updated

typescript
const [count, setCount] = useState<number>(0);
setCount(prev => prev + 1);
useState with object
const [user, setUser] = useState<User>({ name: '', age: 0 })

Managing object state requires spreading previous state for partial updates

typescript
const [form, setForm] = useState({ name: '', email: '' });
setForm(prev => ({ ...prev, name: 'John' }));
useState lazy initialization
const [state, setState] = useState<T>(() => expensiveComputation())

Pass a function to compute initial state only once on mount

typescript
const [data, setData] = useState(() => JSON.parse(localStorage.getItem('data') || '{}'));
useReducer
const [state, dispatch] = useReducer<R>(reducer: R, initialState: ReducerState<R>): [ReducerState<R>, Dispatch<ReducerAction<R>>]

Manages complex state logic with a reducer function similar to Redux

typescript
const reducer = (state, action) => action.type === 'increment' ? { count: state.count + 1 } : state;
const [state, dispatch] = useReducer(reducer, { count: 0 });
useReducer with init
const [state, dispatch] = useReducer(reducer, initialArg, init)

Lazy initialization for useReducer using an init function

typescript
const init = (initialCount) => ({ count: initialCount });
const [state, dispatch] = useReducer(reducer, 0, init);

Effect Hooks

useEffect
useEffect(effect: EffectCallback, deps?: DependencyList): void

Runs side effects after render, with optional cleanup and dependency tracking

typescript
useEffect(() => {
  document.title = `Count: ${count}`;
  return () => console.log('cleanup');
}, [count]);
useEffect mount only
useEffect(() => { /* runs once */ }, [])

Empty dependency array ensures effect runs only on component mount

typescript
useEffect(() => {
  fetchData().then(setData);
}, []);
useEffect with cleanup
useEffect(() => { return () => cleanup(); }, [deps])

Return a cleanup function to run before effect re-runs or on unmount

typescript
useEffect(() => {
  const id = setInterval(() => tick(), 1000);
  return () => clearInterval(id);
}, []);
useLayoutEffect
useLayoutEffect(effect: EffectCallback, deps?: DependencyList): void

Fires synchronously after DOM mutations but before browser paint

typescript
useLayoutEffect(() => {
  const { height } = ref.current.getBoundingClientRect();
  setHeight(height);
}, []);
useInsertionEffect
useInsertionEffect(effect: EffectCallback, deps?: DependencyList): void

Fires before DOM mutations for CSS-in-JS library style injection

typescript
useInsertionEffect(() => {
  const style = document.createElement('style');
  document.head.appendChild(style);
}, []);

Context Hook

useContext
const value = useContext<T>(Context: React.Context<T>): T

Subscribes to a React context and returns its current value

typescript
const ThemeContext = createContext<'light' | 'dark'>('light');
const theme = useContext(ThemeContext);
useContext with provider
<Context.Provider value={value}>{children}</Context.Provider>

Wrap components with Provider to pass context value down the tree

typescript
const UserContext = createContext<User | null>(null);
<UserContext.Provider value={user}>{children}</UserContext.Provider>
useContext custom hook pattern
const useTheme = () => useContext(ThemeContext)

Create a custom hook for cleaner context consumption

typescript
const useAuth = () => {
  const ctx = useContext(AuthContext);
  if (!ctx) throw new Error('Must be within AuthProvider');
  return ctx;
};

Performance Optimization Hooks

useMemo
const memoizedValue = useMemo<T>(() => computeValue(): T, deps: DependencyList): T

Memoizes expensive computations and recalculates only when dependencies change

typescript
const sortedList = useMemo(() => 
  items.sort((a, b) => a.name.localeCompare(b.name)),
[items]);
useCallback
const memoizedCallback = useCallback<T>(callback: T, deps: DependencyList): T

Returns a memoized callback that only changes when dependencies change

typescript
const handleClick = useCallback((id: string) => {
  setSelected(id);
}, []);
useCallback vs useMemo
useCallback(fn, deps) === useMemo(() => fn, deps)

useCallback memoizes functions while useMemo memoizes any computed value

typescript
const fn = useCallback(() => doSomething(a, b), [a, b]);
const fn2 = useMemo(() => () => doSomething(a, b), [a, b]);
useMemo for referential equality
const options = useMemo(() => ({ sortBy, filterBy }), [sortBy, filterBy])

Preserve object reference to prevent unnecessary child re-renders

typescript
const config = useMemo(() => ({
  theme: 'dark',
  locale: userLocale
}), [userLocale]);

Ref Hooks

useRef
const ref = useRef<T>(initialValue: T): MutableRefObject<T>

Creates a mutable ref object that persists across renders without causing re-renders

typescript
const inputRef = useRef<HTMLInputElement>(null);
inputRef.current?.focus();
useRef for DOM elements
const ref = useRef<HTMLElement>(null)

Access DOM nodes directly by attaching ref to JSX elements

typescript
const divRef = useRef<HTMLDivElement>(null);
<div ref={divRef}>Content</div>
useRef for mutable values
const valueRef = useRef<T>(value)

Store mutable values that dont trigger re-renders when changed

typescript
const renderCount = useRef(0);
useEffect(() => { renderCount.current += 1; });
useRef for previous value
const prevRef = useRef<T>()

Track previous prop or state values across renders

typescript
const prevCount = useRef(count);
useEffect(() => { prevCount.current = count; }, [count]);
useImperativeHandle
useImperativeHandle<T, R>(ref: Ref<T>, createHandle: () => R, deps?: DependencyList): void

Customizes the instance value exposed to parent when using forwardRef

typescript
useImperativeHandle(ref, () => ({
  focus: () => inputRef.current?.focus(),
  clear: () => inputRef.current.value = ''
}), []);

Concurrent Rendering Hooks

useTransition
const [isPending, startTransition] = useTransition(): [boolean, TransitionStartFunction]

Marks state updates as non-urgent allowing UI to remain responsive

typescript
const [isPending, startTransition] = useTransition();
startTransition(() => setSearchResults(filterData(query)));
useDeferredValue
const deferredValue = useDeferredValue<T>(value: T): T

Defers updating a value to keep UI responsive during expensive re-renders

typescript
const deferredQuery = useDeferredValue(query);
const results = useMemo(() => search(deferredQuery), [deferredQuery]);
useTransition with loading state
isPending ? <Spinner /> : <Content />

Use isPending to show loading indicators during transitions

typescript
const [isPending, startTransition] = useTransition();
return isPending ? <Loading /> : <List items={items} />;
useDeferredValue vs useTransition
useDeferredValue(value) // for values from props startTransition(() => setState()) // for state you control

Use useDeferredValue for values you dont control, useTransition for state updates

typescript
// Props: const deferred = useDeferredValue(propValue);
// State: startTransition(() => setFilter(newFilter));

Utility Hooks

useId
const id = useId(): string

Generates unique IDs stable across server and client for accessibility attributes

typescript
const id = useId();
<label htmlFor={id}>Name</label>
<input id={id} type="text" />
useId for multiple elements
const id = useId(); // use as prefix

Use a single useId call as prefix for multiple related elements

typescript
const id = useId();
<input id={`${id}-name`} />
<input id={`${id}-email`} />
useDebugValue
useDebugValue<T>(value: T, format?: (value: T) => any): void

Displays a label for custom hooks in React DevTools

typescript
function useOnlineStatus() {
  const isOnline = useSyncExternalStore(subscribe, getSnapshot);
  useDebugValue(isOnline ? 'Online' : 'Offline');
  return isOnline;
}
useDebugValue with formatter
useDebugValue(date, date => date.toISOString())

Pass a format function to defer expensive formatting until DevTools inspects

typescript
useDebugValue(user, u => `${u.name} (${u.role})`);
useSyncExternalStore
const snapshot = useSyncExternalStore<T>(subscribe, getSnapshot, getServerSnapshot?): T

Subscribes to external stores with support for concurrent rendering

typescript
const width = useSyncExternalStore(
  (cb) => { window.addEventListener('resize', cb); return () => window.removeEventListener('resize', cb); },
  () => window.innerWidth
);

Custom Hook Patterns

Custom hook naming
function useCustomHook(): ReturnType { }

Custom hooks must start with use to follow React conventions and enable lint rules

typescript
function useLocalStorage<T>(key: string, initial: T) {
  const [value, setValue] = useState<T>(() => JSON.parse(localStorage.getItem(key) || JSON.stringify(initial)));
  return [value, setValue] as const;
}
useToggle
function useToggle(initial?: boolean): [boolean, () => void]

Custom hook for boolean toggle state management

typescript
function useToggle(initial = false) {
  const [value, setValue] = useState(initial);
  const toggle = useCallback(() => setValue(v => !v), []);
  return [value, toggle] as const;
}
usePrevious
function usePrevious<T>(value: T): T | undefined

Custom hook to track the previous value of a prop or state

typescript
function usePrevious<T>(value: T) {
  const ref = useRef<T>();
  useEffect(() => { ref.current = value; }, [value]);
  return ref.current;
}
useDebounce
function useDebounce<T>(value: T, delay: number): T

Custom hook to debounce a rapidly changing value

typescript
function useDebounce<T>(value: T, delay: number) {
  const [debounced, setDebounced] = useState(value);
  useEffect(() => { const t = setTimeout(() => setDebounced(value), delay); return () => clearTimeout(t); }, [value, delay]);
  return debounced;
}
useOnClickOutside
function useOnClickOutside(ref: RefObject<HTMLElement>, handler: () => void): void

Custom hook to detect clicks outside a referenced element

typescript
function useOnClickOutside(ref, handler) {
  useEffect(() => {
    const listener = (e) => { if (!ref.current?.contains(e.target)) handler(); };
    document.addEventListener('mousedown', listener);
    return () => document.removeEventListener('mousedown', listener);
  }, [ref, handler]);
}
useAsync
function useAsync<T>(asyncFn: () => Promise<T>): { data: T | null; loading: boolean; error: Error | null }

Custom hook for handling async operations with loading and error states

typescript
function useAsync<T>(fn: () => Promise<T>) {
  const [state, setState] = useState({ data: null, loading: true, error: null });
  useEffect(() => { fn().then(data => setState({ data, loading: false, error: null })).catch(error => setState({ data: null, loading: false, error })); }, []);
  return state;
}

Related Content