Skip to main content

React Cheatsheet

Frontend 16 views Apr 2026

React Cheatsheet

Component Basics

// Function component
function Hello({ name, age = 0 }) {
  return <h1>Hello, {name}! Age: {age}</h1>;
}

export default function App() {
  return <Hello name="Alice" age={30} />;
}

// Fragments (avoid extra DOM node)
function List() {
  return (
    <>
      <li>Item 1</li>
      <li>Item 2</li>
    </>
  );
}

// Conditional rendering
function Status({ isOnline }) {
  return (
    <span>
      {isOnline ? "Online" : "Offline"}
      {isOnline && <span className="dot green" />}
    </span>
  );
}

// List rendering — key must be stable!
function Items({ items }) {
  return (
    <ul>
      {items.map((item, i) => (
        <li key={item}>{i+1}. {item}</li>
      ))}
    </ul>
  );
}

useState

import { useState } from "react";

// Primitive state
const [count, setCount] = useState(0);
const [name, setName] = useState("Alice");

// Object state — always spread, never mutate
const [user, setUser] = useState({ name: "Alice", age: 30 });
setUser(prev => ({ ...prev, age: 31 }));

// Array state
const [items, setItems] = useState([]);
setItems(prev => [...prev, "new item"]);
setItems(prev => prev.filter(i => i !== "remove this"));

// Lazy initializer
const [data, setData] = useState(() => computeExpensiveValue());

// Toggle
setActive(prev => !prev);

useEffect

import { useEffect } from "react";

// Run once on mount
useEffect(() => {
  fetchData();
}, []);

// Run when dep changes
useEffect(() => {
  document.title = "Count: " + count;
}, [count]);

// Cleanup (timers, subscriptions, listeners)
useEffect(() => {
  const id = setInterval(() => setCount(c => c + 1), 1000);
  return () => clearInterval(id);
}, []);

// Fetch with abort / cancel guard
useEffect(() => {
  let cancelled = false;
  async function load() {
    const data = await fetchUser(userId);
    if (!cancelled) setUser(data);
  }
  load();
  return () => { cancelled = true; };
}, [userId]);

useRef, useMemo, useCallback

import { useRef, useMemo, useCallback } from "react";

// useRef — DOM access
const inputRef = useRef(null);
inputRef.current?.focus();

// useRef — persistent mutable value (no re-render)
const countRef = useRef(0);
countRef.current++;

// useMemo — expensive computation
const sorted = useMemo(
  () => [...items].sort((a, b) => a.price - b.price),
  [items]
);

// useCallback — stable function reference
const handleDelete = useCallback(
  (id) => dispatch(deleteItem(id)),
  [dispatch]
);

useContext

import { createContext, useContext, useState } from "react";

const ThemeContext = createContext(null);

// Provider component
function ThemeProvider({ children }) {
  const [theme, setTheme] = useState("light");
  const toggle = () => setTheme(t => t === "light" ? "dark" : "light");
  return (
    <ThemeContext.Provider value={{ theme, toggle }}>
      {children}
    </ThemeContext.Provider>
  );
}

// Custom hook
function useTheme() {
  const ctx = useContext(ThemeContext);
  if (!ctx) throw new Error("useTheme must be inside ThemeProvider");
  return ctx;
}

// Consumer
function Button() {
  const { theme, toggle } = useTheme();
  return <button className={theme} onClick={toggle}>Toggle</button>;
}

useReducer

import { useReducer } from "react";

function reducer(state, action) {
  switch (action.type) {
    case "increment": return { ...state, count: state.count + state.step };
    case "decrement": return { ...state, count: state.count - state.step };
    case "reset":     return { count: 0, step: state.step };
    case "setStep":   return { ...state, step: action.payload };
    default:          return state;
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, { count: 0, step: 1 });
  return (
    <>
      <p>Count: {state.count}</p>
      <button onClick={() => dispatch({ type: "increment" })}>+</button>
      <button onClick={() => dispatch({ type: "decrement" })}>-</button>
    </>
  );
}

Custom Hooks

// useFetch
function useFetch(url) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    let cancelled = false;
    setLoading(true);
    fetch(url)
      .then(r => r.json())
      .then(d => { if (!cancelled) { setData(d); setLoading(false); } })
      .catch(e => { if (!cancelled) { setError(e); setLoading(false); } });
    return () => { cancelled = true; };
  }, [url]);

  return { data, loading, error };
}

// useDebounce
function useDebounce(value, delay = 300) {
  const [debounced, setDebounced] = useState(value);
  useEffect(() => {
    const id = setTimeout(() => setDebounced(value), delay);
    return () => clearTimeout(id);
  }, [value, delay]);
  return debounced;
}

// useLocalStorage
function useLocalStorage(key, initial) {
  const [value, setValue] = useState(() => {
    try {
      const stored = localStorage.getItem(key);
      return stored !== null ? JSON.parse(stored) : initial;
    } catch { return initial; }
  });

  const set = useCallback((v) => {
    setValue(prev => {
      const next = typeof v === "function" ? v(prev) : v;
      localStorage.setItem(key, JSON.stringify(next));
      return next;
    });
  }, [key]);

  return [value, set];
}

Performance

// React.memo — skip re-render if props unchanged
const ExpensiveChild = React.memo(function Child({ data }) {
  return <div>{data}</div>;
});

// lazy + Suspense — code splitting
const HeavyPage = React.lazy(() => import("./HeavyPage"));

function App() {
  return (
    <Suspense fallback={<Spinner />}>
      <HeavyPage />
    </Suspense>
  );
}

// useTransition — non-urgent update
const [isPending, startTransition] = useTransition();
startTransition(() => setFilteredList(expensiveFilter(input)));

// useDeferredValue
const deferred = useDeferredValue(searchQuery);

Patterns & Best Practices

// Prop drilling alternative: composition
function Layout({ header, sidebar, children }) {
  return (
    <div className="layout">
      <header>{header}</header>
      <aside>{sidebar}</aside>
      <main>{children}</main>
    </div>
  );
}

// Error boundary (class component still needed)
class ErrorBoundary extends React.Component {
  state = { hasError: false };
  static getDerivedStateFromError() { return { hasError: true }; }
  componentDidCatch(error, info) { console.error(error, info); }
  render() {
    return this.state.hasError
      ? <h1>Something went wrong.</h1>
      : this.props.children;
  }
}

// forwardRef
const Input = React.forwardRef(({ label, ...props }, ref) => (
  <label>
    {label}
    <input ref={ref} {...props} />
  </label>
));

// useImperativeHandle
React.useImperativeHandle(ref, () => ({
  focus: () => inputRef.current.focus(),
  clear: () => setvalue("")
}));

Found this helpful? Share it!

Tweet LinkedIn WhatsApp
Translate Page