Hooks

Motivation

  • Allow you to use state and effects in functions not classes

  • Reuse state logic

  • Have one block of effects for one purpose with clean up and everything

  • Allows easier reuse of stateful logic often done with patterns like higher-order components and render props.

Basics

  • Only call Hooks at the top level. Don’t call Hooks inside loops, conditions, or nested functions.

  • Only call Hooks from React function components.

  • Functions/useCallbacks must be above their use in dependency arrays or else they DONT ACTUALLY CAUSE A RERENDER(cuz hoisting)

All Hooks

  • useState => See state.md

  • useEffect => See effect.md

  • useContext

    • Still need MyContext.Provider above

    const value = useContext(MyContext);
  • useReducer

    • If one element of your state relies on the value of another element of your state, then it's almost always best to use useReducer

  • useCallback

    Not actually cheaper than defining function by itself. But makes the reference the same for React's equality check if you use the ft in another hook or as a prop to avoid reruns

    • function MeasureExample() {
        const [height, setHeight] = useState(0);
      
        const measuredRef = useCallback(node => {
          if (node !== null) {
            setHeight(node.getBoundingClientRect().height);
          }
        }, []);
      
        return (
          <>
            <h1 ref={measuredRef}>Hello, world</h1>
            <h2>The above header is {Math.round(height)}px tall</h2>
          </>
        );
      }

      Unlike useRef, notifies us on change of ref

  • useMemo

    const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

    
    - Your app must still work perfectly well (maybe a bit slow though) if `useMemo` calls your callback on every render.
    
    - Introduces overhead, but for expensive pure computations
    
  • useRef

    • function TextInputWithFocusButton() {
        const inputEl = useRef(null);
        const onButtonClick = () => {
          // `current` points to the mounted text input element
          inputEl.current.focus();
        };
        return (
          <>
            <input ref={inputEl} type="text" />
            <button onClick={onButtonClick}>Focus the input</button>
          </>
        );
      }

      Can also be equivalent to instance variables in class components. Mutating the .current property won’t cause a re-render.

      Updating a ref is a side effect so it should be done only inside an useEffect (or useLayoutEffect) or inside an event handler.

  • useTransition

    New in v18

    const [isPending, startTransition] = useTransition();
    
    setInputValue(input);
    startTransition(() => {
      setSearchQuery(input);
    });

    For large UI transitions to mark them as interruptable so typing and stuff doesnt freeze it

Custom Hooks

useWindowWidth

function useWindowWidth() {
  const [width, setWidth] = useState(window.innerWidth);
  
  useEffect(() => {
    const handleResize = () => setWidth(window.innerWidth);
    window.addEventListener('resize', handleResize);
    return () => {
      window.removeEventListener('resize', handleResize);
    };
  });
  
  return width;
}
function MyResponsiveComponent() {
  const width = useWindowWidth(); // Our custom Hook
  return (
    <p>Window width is {width}</p>
  );
}

useDebounce

import { useEffect, useState } from "react";

function useDebounce(value, delay = 500) {
  const [debouncedVal, setDebouncedVal] = useState(value);

  useEffect(() => {
    const timeoutID = setTimeout(() => setDebouncedVal(value), delay);
    return () => clearTimeout(timeoutID);
  }, [value, delay]);

  return debouncedVal;
}

export default useDebounce;

useRandomId

export function useRandomId() {
  const randomId = useMemo(() => {
    return "_" + Math.random().toString(36).slice(2, 9);
  }, []);

  return randomId;
}

Last updated