Most Important React Hooks and How to Use Them

May 1, 2024

1. useState

The useState Hook is used to add state to functional components. It returns an array with two elements: the current state value and a function to update it.

import React, { useState } from 'react';

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

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}
  • Use destructuring to access the state and the updater function.
  • You can pass an initial state value to useState.

2. useEffect

The useEffect Hook lets you perform side effects in your components, such as fetching data, setting up subscriptions, or manually changing the DOM. It runs after the render.

import React, { useEffect, useState } from 'react';

function DataFetcher() {
  const [data, setData] = useState(null);

  useEffect(() => {
    fetch('https://api.yelnil.com/data')
      .then(response => response.json())
      .then(data => setData(data));
  }, []); // Empty array means this effect runs once after the initial render

  return (
    <div>
      <pre>{JSON.stringify(data, null, 2)}</pre>
    </div>
  );
}
  • The second argument to useEffect is the dependency array. If it’s empty, the effect runs only once after the initial render.
  • Be mindful of dependencies to avoid infinite loops.

3. useContext

The useContext Hook is used to subscribe to React context without introducing nesting.

import React, { useContext } from 'react';
import { ThemeContext } from './ThemeContext';

function ThemedButton() {
  const theme = useContext(ThemeContext);

  return (
    <button style={{ background: theme.background, color: theme.color }}>
      Themed Button
    </button>
  );
}
  • Use useContext to consume context values directly.
  • Ensure the context provider wraps the component tree.

4. useReducer

The useReducer Hook is used for managing more complex state logic. It’s an alternative to useState.

import React, { useReducer } from 'react';

const initialState = { count: 0 };

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    default:
      throw new Error();
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <div>
      <p>Count: {state.count}</p>
      <button onClick={() => dispatch({ type: 'increment' })}>+</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
    </div>
  );
}

  • Use useReducer for complex state logic or when state depends on previous states.
  • Reducers are pure functions that return a new state based on the action.

5. useMemo

The useMemo Hook memoizes expensive computations, recalculating them only when their dependencies change.

import React, { useMemo, useState } from 'react';

function ExpensiveCalculationComponent({ num }) {
  const [count, setCount] = useState(0);

  const expensiveCalculation = useMemo(() => {
    console.log('Calculating...');
    return num * 2;
  }, [num]);

  return (
    <div>
      <p>Result: {expensiveCalculation}</p>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}
  • Use useMemo to optimize performance for expensive calculations.
  • Only use it when necessary to avoid unnecessary complexity.

6. useCallback

The useCallback Hook returns a memoized version of the callback that only changes if one of the dependencies has changed. It is useful for passing callbacks to optimized child components that rely on reference equality to prevent unnecessary renders.

Example:

import React, { useState, useCallback } from 'react';

function Child({ increment }) {
  console.log('Child rendered');
  return (
    <button onClick={increment}>Increment</button>
  );
}

function Parent() {
  const [count, setCount] = useState(0);

  const increment = useCallback(() => {
    setCount(c => c + 1);
  }, []);

  return (
    <div>
      <p>Count: {count}</p>
      <Child increment={increment} />
    </div>
  );
}

  • Use useCallback to memoize functions that are passed as props to child components.
  • Helps prevent unnecessary renders in child components.

7. Your Own Hook

Custom Hooks allow you to extract and reuse stateful logic across multiple components. They are a way to encapsulate related logic into reusable functions.

import React, { useState, useEffect } from 'react';

// Your custom hook - useFetch
function useFetch(url) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetch(url)
      .then(response => response.json())
      .then(data => {
        setData(data);
        setLoading(false);
      });
  }, [url]);

  return { data, loading };
}

// How to use your custom Hook
function DataFetcher({ url }) {
  const { data, loading } = useFetch(url);

  if (loading) return <p>Loading...</p>;

  return (
    <div>
      <pre>{JSON.stringify(data, null, 2)}</pre>
    </div>
  );
}
  • Name custom Hooks starting with "use" to ensure they follow the Hook rules.
  • Use custom Hooks to encapsulate and reuse common logic across multiple components.
  • Custom Hooks can call other Hooks to build more complex functionality.


A lot more React Hooks are available, each serving a specific purpose, but understanding these core Hooks will help you build more efficient and maintainable React applications.

For more readings, visit the official React Hooks page!