DEV Community

Cover image for Ultimate React Hooks Guide: Best Practices & Examples
TenE
TenE

Posted on

Ultimate React Hooks Guide: Best Practices & Examples

React Hooks are functions that let you "hook into" React state and lifecycle features from function components. They were introduced in React 16.8. Below is a list of common React hooks, their explanations, and examples of usage. Additionally, this guide covers how to create custom hooks.

1. useState

The useState hook lets you add state to function components.

Explanation

useState returns an array with two elements: the current state value and a function to update it.

Usage

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

2. useEffect

The useEffect hook lets you perform side effects in function components.

useEffect With Dependencies

  • useEffect runs after the first render and after every update. You can also specify dependencies to control when the effect runs.

Usage

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

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

  useEffect(() => {
    document.title = `You clicked ${count} times`;
  }, [count]);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

useEffect With an Empty Dependency Array []

  • Runs only once after the initial render (similar to componentDidMount).
useEffect(() => {
  console.log('This runs only once after the first render');
}, []);
Enter fullscreen mode Exit fullscreen mode
  • Use case: Fetching data when the component mounts, setting up event listeners, etc.

useEffect Without Dependencies

  • Runs on every render (initial and subsequent re-renders).
useEffect(() => {
  console.log('This runs after every render');
});
Enter fullscreen mode Exit fullscreen mode
  • Use case: Running code that doesn’t depend on specific state changes, like logging or animations.

Cleanup Function in useEffect

  • If useEffect returns a function, it acts as a cleanup that runs before the next effect or when the component unmounts.
useEffect(() => {
  const interval = setInterval(() => {
    console.log('Interval running...');
  }, 1000);

  return () => {
    clearInterval(interval); // Cleanup on unmount
  };
}, []);
Enter fullscreen mode Exit fullscreen mode
  • Use case: Cleaning up event listeners, canceling API calls, clearing intervals, etc.

3. useContext

The useContext hook lets you access the value of a context.

Explanation

useContext accepts a context object (the value returned from React.createContext) and returns the current context value.

Usage

import React, { useContext } from 'react';

const MyContext = React.createContext();

function MyComponent() {
  const value = useContext(MyContext);

  return <div>{value}</div>;
}

function App() {
  return (
    <MyContext.Provider value="Hello, World!">
      <MyComponent />
    </MyContext.Provider>
  );
}
Enter fullscreen mode Exit fullscreen mode

4. useReducer

The useReducer hook is an alternative to useState for managing complex state logic.

Explanation

useReducer returns an array with the current state and a dispatch function to trigger state updates.

Usage

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' })}>
        Increment
      </button>
      <button onClick={() => dispatch({ type: 'decrement' })}>
        Decrement
      </button>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

5. useCallback

The useCallback hook returns a memoized callback.

Explanation

useCallback returns a memoized version of the callback that only changes if one of the dependencies has changed.

Usage

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

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

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

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={increment}>
        Click me
      </button>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

6. useMemo

The useMemo hook returns a memoized value.

Explanation

useMemo only recomputes the memoized value when one of the dependencies has changed.

Usage

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

function ExpensiveCalculationComponent() {
  const [count, setCount] = useState(0);
  const [value, setValue] = useState('');

  const expensiveCalculation = useMemo(() => {
    // Assume this is an expensive calculation
    return count * 2;
  }, [count]);

  return (
    <div>
      <p>Expensive Calculation: {expensiveCalculation}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
      <input value={value} onChange={(e) => setValue(e.target.value)} />
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

7. useRef

The useRef hook returns a mutable ref object.

Explanation

useRef is useful for accessing DOM elements or persisting values across renders.

Usage

import React, { useRef } from 'react';

function TextInputWithFocusButton() {
  const inputEl = useRef(null);
  const onButtonClick = () => {
    inputEl.current.focus();
  };

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

8. useImperativeHandle

The useImperativeHandle hook customizes the instance value that is exposed when using ref.

Explanation

useImperativeHandle should be used with React.forwardRef.

Usage

import React, { useRef, useImperativeHandle, forwardRef } from 'react';

const FancyInput = forwardRef((props, ref) => {
  const inputRef = useRef();

  useImperativeHandle(ref, () => ({
    focus: () => {
      inputRef.current.focus();
    }
  }));

  return <input ref={inputRef} />;
});

function Parent() {
  const inputRef = useRef();

  return (
    <div>
      <FancyInput ref={inputRef} />
      <button onClick={() => inputRef.current.focus()}>Focus Input</button>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

9. useLayoutEffect

The useLayoutEffect hook runs after all DOM mutations.

Explanation

useLayoutEffect is similar to useEffect, but it fires synchronously after all DOM mutations.

Usage

import React, { useLayoutEffect, useRef } from 'react';

function LayoutEffectComponent() {
  const divRef = useRef();

  useLayoutEffect(() => {
    console.log(divRef.current.getBoundingClientRect());
  });

  return <div ref={divRef}>Hello, world!</div>;
}
Enter fullscreen mode Exit fullscreen mode

10. useDebugValue

The useDebugValue hook is used to display a label for custom hooks in React DevTools.

Explanation

useDebugValue can be helpful for debugging custom hooks.

Usage

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

function useFriendStatus(friendID) {
  const [isOnline, setIsOnline] = useState(null);

  useDebugValue(isOnline ? 'Online' : 'Offline');

  // Imagine this is an actual implementation
  return isOnline;
}
Enter fullscreen mode Exit fullscreen mode

11. Custom Hooks

Custom hooks let you extract component logic into reusable functions.

Explanation

Custom hooks are normal JavaScript functions that can call other hooks.

Example: useFetch

import { useState, useEffect } from 'react';

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 };
}

export default useFetch;
Enter fullscreen mode Exit fullscreen mode

Usage of Custom Hook

import React from 'react';
import useFetch from './useFetch';

function App() {
  const { data, loading } = useFetch('https://api.example.com/data');

  if (loading) {
    return <div>Loading...</div>;
  }

  return (
    <div>
      <pre>{JSON.stringify(data, null, 2)}</pre>
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

This guide provides a comprehensive overview of React hooks, their usage, and how to create custom hooks.

Tiugo image

Modular, Fast, and Built for Developers

CKEditor 5 gives you full control over your editing experience. A modular architecture means you get high performance, fewer re-renders and a setup that scales with your needs.

Start now

Top comments (0)