Understanding React Hooks

React Hooks are a feature introduced in React 16.8 that allow you to use state and other React features without writing a class. They enable functional components to have access to state and other React capabilities that were previously only possible with class components.

Algogenz logo

10m · 9min read

React Hooks are a feature introduced in React 16.8 that allow you to use state and other React features without writing a class. They enable functional components to have access to state and other React capabilities that were previously only possible with class components. Hooks are not just a way to use state in functional components, but also a means to organize logic in a more modular, readable, and reusable way.


Why Hooks?

Before Hooks, React had functional components, which were stateless, and class components, which could hold state and lifecycle methods. React developers often found it difficult to reuse stateful logic across components and encountered complexities in managing the lifecycle methods in class components. Hooks were introduced to solve these problems by allowing state and lifecycle features to be used within functional components.

 

useState Hook

The useState hook is used to add state to functional components. Here's a simple example:


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

In this example, useState(0) initializes the count state to zero. The count variable represents the current state, and setCount is a function that updates the state. When the button is clicked, setCount is called with the new state, causing the component to re-render with the updated state.


useEffect Hook

The useEffect hook is used to perform side effects in functional components. It's similar to componentDidMountcomponentDidUpdate, and componentWillUnmount in class components. Here's a simple example:


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

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

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

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

In this example, useEffect is called after the component renders. Inside useEffect, we update the document title with the current count. This effect runs whenever the component renders, so the title always reflects the latest count.


useCallback Hook

The useCallback hook returns a memoized version of the callback that only changes if one of the dependencies has changed. It's useful when passing callbacks to optimized child components that rely on reference equality to prevent unnecessary renders. Here's a simple example:


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

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

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

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

In this example, useCallback is used to create a memoized version of the increment function that only changes when count changes. This ensures that the increment function doesn't change unnecessarily, preventing potential issues with child components that depend on reference equality.


useMemo Hook

The useMemo hook returns a memoized value. It's used to optimize performance by memoizing expensive computations. Here's a simple example:


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

function Fibonacci({ n }) {
 const [num, setNum] = useState(n);

 const fib = useMemo(() => {
   let a = 0, b = 1;
   for (let i = 0; i < num; i++) {
     [a, b] = [b, a + b];
   }
   return a;
 }, [num]);

 return (
   <div>
     <p>The {num}th number in the Fibonacci sequence is {fib}.</p>
     <input value={num} onChange={e => setNum(Number(e.target.value))} />
   </div>
 );
};

In this example, useMemo is used to compute the nth number in the Fibonacci sequence. The computation is only re-run when num changes, improving performance for large numbers.

These are just a few examples of the common React hooks. There are many other hooks available, including useReduceruseContextuseRef, and more. Each hook serves a different purpose and can be used to solve different types of problems in your React applications.


useReducer Hook

The useReducer hook is used for state management, especially when the next state depends on the previous one. It's similar to reduce in JavaScript. It's usually preferable over useState when you have complex state logic that involves multiple sub-values or when the next state depends on the previous one. Here's a simple example:


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 (
   <>
     Count: {state.count}
     <button onClick={() => dispatch({type: 'increment'})}>+</button>
     <button onClick={() => dispatch({type: 'decrement'})}>-</button>
   </>
 );
};

In this example, useReducer is used to manage the count state. The reducer function handles the actions and returns the new state based on the current state and the action. The dispatch function is used to send actions to the reducer.


useContext Hook

The useContext hook is used to access the value of a context. It's similar to Context.Consumer but with a shorter syntax. Here's a simple example:


import React, { useContext } from 'react';
const ThemeContext = React.createContext('light');

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

 return <div>Current theme is: {theme}</div>;
};

In this example, useContext(ThemeContext) is used to access the current value of the ThemeContext. The Display component displays the current theme.


useRef Hook

The useRef hook is used to create a mutable ref object. The .current property is initialized with the passed argument and persists for the full lifetime of the component. Here's a simple example:


import React, { useRef } from 'react';

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

In this example, useRef is used to create a ref for the input element. The onButtonClick function focuses the input when the button is clicked.


useLayoutEffect Hook

The useLayoutEffect hook is identical to useEffect, but it fires synchronously after all DOM mutations. This can be useful for reading layout from the DOM and synchronously re-rendering. Here's a simple example:


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

function Component() {
 const [width, setWidth] = useState(0);

 useLayoutEffect(() => {
   setWidth(document.getElementById('content').offsetWidth);
 }, []);

 return <div id="content">Content width is {width}</div>;
};

In this example, useLayoutEffect is used to measure the width of the content area immediately after the DOM is updated, ensuring that the measurement is accurate.

 

useId

useId generates unique IDs that are stable across server and client, and helps avoid hydration mismatches.


import React, { useId } from 'react';

function FormField() {
  const id = useId();

  return (
    <><label htmlFor={id}>Enter your name:</label><input id={id} /></>
  );
};

In this example, useId provides a unique ID for the label and input relationship.

 

useInsertionEffect

The useInsertionEffect hook is used to inject <style> tags into the DOM before layout effects fire. This is particularly useful for CSS-in-JS libraries, which often generate new rules on the fly and insert them into the document. However, inserting styles during rendering can be slow, as it forces the browser to recalculate the styles frequently. The useInsertionEffect hook helps to solve this problem by inserting the styles before any layout effects fire.

Let's see an example where useInsertionEffect is used to inject <style> tags into the DOM:


import { useInsertionEffect } from 'react';

let isInserted = new Set();

function useCSS(rule) {
 useInsertionEffect(() => {
   if (!isInserted.has(rule)) {
     isInserted.add(rule);
     const styleElement = document.createElement('style');
     styleElement.textContent = rule;
     document.head.appendChild(styleElement);
   }
 });
 return rule;
}

function Button() {
 const className = useCSS('.button { color: blue; }');
 return <button className={className}>Hello</button>;
};

In this example:

  • We define a Set called isInserted to keep track of which rules have already been inserted into the DOM.
  • The useCSS function takes a CSS rule as an argument. It uses useInsertionEffect to append a new <style> element to the <head> of the document. The text content of the <style> element is set to the CSS rule.
  • Before appending the <style> element, we check whether the rule has already been inserted by checking if isInserted contains the rule. If it doesn't, we add the rule to isInserted and append the <style> element.
  • The Button component uses the useCSS function to apply a CSS rule to a button.

Remember, useInsertionEffect only runs on the client, not during server rendering. Also, you can't update state from inside useInsertionEffect. And by the time useInsertionEffect runs, refs are not attached yet, and the DOM is not yet updated. Therefore, you shouldn't rely on the DOM being updated at any particular time.

 

Custom Hooks

Custom hooks are a mechanism to reuse stateful logic. They allow you to abstract component logic into reusable functions. Here's a simple example:


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

function Component() {
 const { data, loading } = useFetch('/api/data');

 if (loading) {
   return 'Loading...';
 }

 return <div>{JSON.stringify(data)}</div>;
};

In this example, useFetch is a custom hook that fetches data from a URL and returns the data and loading state. The useFetch hook is then used in the  Component to fetch data from '/api/data'.


useImperativeHandle Hook

The useImperativeHandle hook is used to customize the instance value that is exposed to parent components when using ref. Here's a simple example:


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

function FancyInput(props, ref) {
 let inputRef = useRef();
 useImperativeHandle(ref, () => ({
   focus: () => {
     inputRef.current.focus();
   }
 }));
 return <input ref={inputRef} />;
}

FancyInput = React.forwardRef(FancyInput); 

In this example,  useImperativeHandle is used to expose a focus function to parent components through a ref. The focus function focuses the input field.


useDebugValue Hook

The useDebugValue hook is used to display a label for custom hooks in React DevTools. It's not commonly used, but can be helpful for debugging custom hooks. Here's a simple example:


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

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

 // Show a label in DevTools next to this Hook// e.g. "FriendStatus: Online"useDebugValue(isOnline ? 'Online' : 'Offline');

 return isOnline;
};

In this example, useDebugValue is used to display the current status of a friend in React DevTools.


Conclusion

These are some of the most commonly used React hooks. Each hook serves a different purpose and can be used to solve different types of problems in your React applications. Remember to follow the rules of hooks: only call hooks at the top level and only call hooks from React function components or other hooks.

Recommended

Next pages: