Tags: react, hooks, events, context, memo, refs, portals, frontend Last updated: 2026-06-26

React Cheatsheet

Quick Reference

ConceptKey API
State useState / useReducer
Side effects useEffect / useLayoutEffect
Context createContext / useContext
Refs useRef / forwardRef
Memoisation useMemo / useCallback / memo
Portals createPortal
Custom hook function useX() {}
Event handling onClick, onChange, etc.
Form values event.target.value
Persisted ref event.persist() (classic)

Hooks (All Built-in)

useState

const [count, setCount] = useState(0);
const [user, setUser] = useState({ name: "", age: 0 });

setCount(42);                     // Replace value
setCount(c => c + 1);             // Functional update
setUser(prev => ({ ...prev, name: "Max" }));

useEffect

useEffect(() => {
  // Run on mount + every render
});

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

useEffect(() => {
  // Run when count changes
  return () => { /* cleanup */ };
}, [count]);

useEffect(() => {
  const id = setInterval(() => {}, 1000);
  return () => clearInterval(id); // cleanup on unmount
}, []);

useLayoutEffect

// Fires synchronously after DOM mutations but before paint.
// Use when you need to measure layout or avoid visual flicker.
useLayoutEffect(() => {
  ref.current.scrollTop = 0;
}, [messages]);

useReducer

const reducer = (state, action) => {
  switch (action.type) {
    case "inc": return { count: state.count + 1 };
    case "dec": return { count: state.count - 1 };
    default: return state;
  }
};
const [state, dispatch] = useReducer(reducer, { count: 0 });
dispatch({ type: "inc" });

useContext

const ThemeCtx = createContext("light");
const theme = useContext(ThemeCtx);

useRef

const inputRef = useRef(null);
const renderCount = useRef(0);
renderCount.current += 1;

// Attach to DOM element
<input ref={inputRef} />
// Read later
inputRef.current.focus();

useMemo

// Recomputes only when deps change
const sorted = useMemo(() => {
  return items.sort((a, b) =>
    a.name.localeCompare(b.name));
}, [items]);

useCallback

// Stable function reference across renders
const handleClick = useCallback((id) => {
  setSelected(id);
}, []);

useImperativeHandle

// Expose a custom API to parent via ref
useImperativeHandle(ref, () => ({
  focus: () => inputRef.current.focus(),
  reset: () => setValue(""),
}));

useId

// Stable unique ID for accessibility (React 18+)
const id = useId();
<label htmlFor={id}>Name</label>
<input id={id} />

useDeferredValue / useTransition (React 18+)

const deferred = useDeferredValue(searchTerm);
const [isPending, startTransition] = useTransition();
startTransition(() => setSearchTerm(value));

useDebugValue

// Labels custom hooks in React DevTools
useDebugValue(isOnline ? "Online" : "Offline");

Rules of Hooks

Synthetic Events

React wraps native browser events in a cross-browser SyntheticEvent.

Common Event Handlers

<button onClick={(e) => handleClick(e)}>Click</button>
<input onChange={(e) => setName(e.target.value)} />
<form onSubmit={(e) => { e.preventDefault(); submit(); }}>

Key Properties

e.target           // Element that triggered the event
e.currentTarget    // Element the handler is attached to
e.preventDefault() // Cancel default behaviour
e.stopPropagation()// Stop bubbling
e.nativeEvent      // Underlying browser event (rarely needed)
e.type             // e.g. "click", "keydown"

Keyboard Events

<input
  onKeyDown={(e) => {
    if (e.key === "Escape") close();
    if (e.key === "Enter") submit();
    if (e.ctrlKey && e.key === "s") save();
  }}
/>

Focus & Blur

// onFocus / onBlur bubble (React 17+ uses native
// focusin/focusout)
<div onFocus={() => setFocused(true)}
     onBlur={() => setFocused(false)}>
  <input />
</div>

Pointer / Mouse

onMouseEnter / onMouseLeave   // Don't bubble from children
onMouseOver / onMouseOut       // Do bubble
onPointerDown / onPointerUp    // Unified mouse + touch + pen

Context API

Create & Provide

const ThemeCtx = createContext("light");   // default value

function App() {
  return (
    <ThemeCtx.Provider value="dark">
      <Toolbar />
    </ThemeCtx.Provider>
  );
}

Consume

// Hook (preferred)
const theme = useContext(ThemeCtx);

// Render-prop (class components)
<ThemeCtx.Consumer>
  {theme => <div className={theme}>...</div>}
</ThemeCtx.Consumer>

Context with State

const UserCtx = createContext(null);
const SetUserCtx = createContext(null);

function UserProvider({ children }) {
  const [user, setUser] = useState(null);
  return (
    <UserCtx.Provider value={user}>
      <SetUserCtx.Provider value={setUser}>
        {children}
      </SetUserCtx.Provider>
    </UserCtx.Provider>
  );
}

Context Performance Tip

// Memoise the value object to avoid re-rendering
// all consumers
const value = useMemo(() => ({ user, setUser }), [user]);
<UserCtx.Provider value={value}>...</UserCtx.Provider>

Memoisation

React.memo (Component)

// Shallow-compare props; skip re-render if unchanged
const Greeting = React.memo(function Greeting({ name }) {
  return <h1>Hello {name}</h1>;
});

// Custom comparison
const Greeting = React.memo(
  function Greeting({ user }) {
    return <h1>{user.name}</h1>;
  },
  (prev, next) => prev.user.id === next.user.id
);

useMemo (Value)

// Avoid recomputing expensive calculations
const visibleItems = useMemo(
  () => items.filter(i => i.score > threshold),
  [items, threshold]
);

useCallback (Function)

// Stable callback — essential when passing to memoised
// children
const onDelete = useCallback((id) => {
  setItems(prev => prev.filter(i => i.id !== id));
}, []);

When to Use

Refs

DOM Refs

const inputRef = useRef(null);
<input ref={inputRef} />
// Focus imperatively
inputRef.current?.focus();
// Measure layout
const rect = inputRef.current?.getBoundingClientRect();

Mutable Values (No Re-render)

const prevValue = useRef(null);
useEffect(() => {
  prevValue.current = value;
});
const changed = prevValue.current !== value;

forwardRef

const FancyInput = forwardRef((props, ref) => {
  return <input ref={ref} className="fancy"
    {...props} />;
});

// Parent
const ref = useRef(null);
<FancyInput ref={ref} />;
ref.current.focus();

Callback Refs

// Called when the ref is attached or detached
<div ref={(node) => {
  if (node) {
    node.scrollTop = node.scrollHeight;
    // measure, attach listeners, etc.
  }
}} />

Ref Cleanup (React 19+)

<div ref={(node) => {
  if (node) {
    const observer = new ResizeObserver(callback);
    observer.observe(node);
    return () => observer.disconnect();  // cleanup
  }
}} />

Portals

Portals render children into a DOM node outside the parent's hierarchy while preserving React's event bubbling.

Basic Portal

import { createPortal } from "react-dom";

function Modal({ children }) {
  return createPortal(
    <div className="modal-overlay">{children}</div>,
    document.getElementById("portal-root")
    // or document.body
  );
}

Portal with Events

// Events still bubble through the React tree, NOT the
// DOM tree. A click inside the portal still triggers
// onClick on the parent component in React, even though
// the DOM is elsewhere.

function App() {
  return (
    <div onClick={() => console.log("bubbled!")}>
      <Modal>
        <button>Click me</button>
      </Modal>
    </div>
  );
}

Common Portal Uses

Custom Hooks

// Fetch wrapper
function useFetch(url) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  useEffect(() => {
    let cancelled = false;
    fetch(url).then(r => r.json()).then(d => {
      if (!cancelled) { setData(d); setLoading(false); }
    });
    return () => { cancelled = true; };
  }, [url]);
  return { data, loading };
}

// Window size
function useWindowSize() {
  const [size, setSize] = useState({
    w: window.innerWidth, h: window.innerHeight
  });
  useEffect(() => {
    const handler = () => setSize({
      w: window.innerWidth, h: window.innerHeight
    });
    window.addEventListener("resize", handler);
    return () =>
      window.removeEventListener("resize", handler);
  }, []);
  return size;
}

Tips