Skip to main content

Command Palette

Search for a command to run...

Understanding React Hooks

Updated
•3 min read•View as Markdown
Understanding React Hooks

In the early days of React, functional components were not efficient. If you needed state or lifecycle methods, you had to refactor to a Class component. Then, hooks arrived, and everything changed.

Today, we’re diving deep into the six most essential hooks that will turn you into a React pro.


1. useState: The Heartbeat of Your Component

The useState hook allows functional components to maintain a local state. When the state changes, React automatically re-renders the component to reflect the new data.

import React, { useState } from 'react';

const Counter = () => {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>Current Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
};

Always use the functional update setCount(prev => prev + 1) if your new state depends on the previous one to avoid stale closures.


2. useEffect: Managing Side Effects

useEffect is your go-to for anything that happens outside the scope of the component’s return, like API calls, subscriptions, or manual DOM updates.

useEffect(() => {
  const fetchData = async () => {
    const response = await fetch('https://api.example.com/data');
    const data = await response.json();
    setData(data);
  };

  fetchData();
}, []); // Empty array means this runs once on mount

3. useRef: Keep a Value Without Re-render

useRef returns a mutable ref object. It’s unique because changing the .current property does not trigger a re-render. Use it to access DOM elements or store persistent values.

const TextInputWithFocusButton = () => {
  const inputEl = useRef(null);

  const onButtonClick = () => {
    // Accessing the DOM directly
    inputEl.current.focus();
  };

  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>
  );
};

4. useMemo: Avoid Repeating Heavy Work

Computing expensive values on every render can lag your app. useMemo caches (memoizes) the result of a calculation and only re-calculates it when dependencies change.

const expensiveResult = useMemo(() => {
  return performHeavyCalculation(data);
}, [data]); // Only re-runs if 'data' changes

5. useCallback: Memoizing Functions

While useMemo caches a value, useCallback caches a function instance. This is crucial when passing callbacks to optimized child components that rely on reference equality to prevent unnecessary re-renders.

const memoizedCallback = useCallback(() => {
  doSomething(a, b);
}, [a, b]);

6. useContext: Share Data Across Components

Tired of passing data through five layers of components? useContext allows you to share data directly to the components that need it.

const ThemeContext = React.createContext('light');

const Display = () => {
  const theme = useContext(ThemeContext);
  return <div className={theme}>The current theme is {theme}</div>;
};

Finally

Don’t use all hooks everywhere.

  • Start with useState and useEffect

  • Use useMemo and useCallback only when needed

  • Keep things simple first