Learn All React Hooks With Example

React Hooks are functions that allow developers to control state and lifecycle features from function components.

Harry
12 min readJan 18, 2024

React Hooks are functions that let you hook into React state and lifecycle features from function components. Introduced in React 16.8, these functions include “useState”, “useEffect”, “useContext”, etc. With these hooks, you’re able to control state and other React features without having to create classes. It enables developers to leverage state and other React capabilities without creating a class. This makes it easier to write and manage React components.

React Hooks Key Takeaways

  • Introduced in React 16.8, these hooks include “useState”, “useEffect”, and “useContext”.
    • UseState hook: Allows state usage in functional components, returns an array with two entries: a value and a function to update that value.
    • UseEffect hook: Allows side effects in function components, including data fetching, subscribing to event handlers, manual DOM mutations, timers.
    • UseContext hook: Allows use of the value of a given context, accepts a context object, and returns the current context value.
    • UseReducer hook: Manages complex state or situations where the next state depends on the previous one.
    • UseCallback hook: Memoizes a callback function, optimizes performance, prevents unnecessary re-rendering of components that depend on the callback.
    • UseMemo hook: Memoizes a value in React, helpful for costly computations.
    • UseRef hook: Allows creation of a mutable value that persists across renders of a functional component.
    • UseLayoutEffect hook: Runs synchronously after the DOM has been updated but before the browser has finished painting the screen.
    • UseDebugValue: Provides additional debug information about a custom hook.
    • UseImperativeHandle: Customizes the instance value exposed to parent components when using forwardRef.

Read: React Js Clean Code Guide

useState hook

Photo by Ferenc Almasi on Unsplash

The useState hook is a special function that lets you use state in functional components. It returns an array with two entries. The first is a value (representing the current state), and the second is a function to update that value.

Here is a basic example of using the useState hook to control the state of a counter:

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

export default Counter;

In this example, useState(0) argument is the initial state. The count variable holds the current state, and setCount is a function to update this state. When the button is clicked, it calls the setCount function with the new count (count + 1), which triggers a re-render of the Counter component with the new state.

useEffect hook

Photo by Lautaro Andreani on Unsplash

The useEffect hook allows you to perform side effects in function components. Side effects include data fetching, subscribing to event handlers, manual DOM mutations, timers, etc. By using this Hook, you tell React that your component needs to do something after render.

Here’s an example of the useEffect hook:

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

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

// Similar to componentDidMount and componentDidUpdate:
useEffect(() => {
// Update the document title using the browser API
document.title = `You clicked ${count} times`;

// Optional: Specify how to clean up after this effect:
return function cleanup() {
document.title = `React App`;
};
}, [count]); // Only re-run the effect if count changes

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

In this example, the useEffect function runs after every render when the count state changes. Inside useEffect, we update the document title using the count. The cleanup function resets the title when the component unmounts or before the next effect run (because count changed).
React skips applying an effect if certain values (specified as second argument array) haven’t changed between re-renders.

useContext hook

The useContext hook is a function that lets you use the value of a given context. It accepts a context object (the value returned from React.createContext) and returns the current context value for that context.

This hook can make it easier to access values from a provider, without having to use a Consumer or wrap components in it.

Here’s an example:

import React, { useContext } from 'react';

// Create a Context
const NumberContext = React.createContext();

// It returns a Provider and a Consumer.
// In functional component, we can skip the Consumer and use the useContext hook.

function Display() {
// useContext accepts a context object and returns the current context value.
const value = useContext(NumberContext);
return <div>The answer is {value}.</div>;
}

function App() {
return (
// Use the Provider to make a value available to all
// children and grandchildren
<NumberContext.Provider value={42}>
<div>
<Display />
</div>
</NumberContext.Provider>
);
}

export default App;

In this example, a context is created with React.createContext(). Inside the App Component, NumberContext.Provider is used which makes the value available to all children. The Display component uses the useContext hook to get the current context value. The value is 42, which was passed by the Provider.

useReducer hook

The useReducer hook is used for managing complex state or situations in which the next state depends on the previous one. It is somewhat similar to using useState but is preferable in cases of complex logic and transitions.

It expects a reducer function and the initial state as arguments, and it returns an array with two elements: the current state and a dispatch method (used for dispatching actions).

Here’s a simple counter example using useReducer:

import React, { useReducer } from 'react';

const initialState = 0;

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

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

return (
<>
Count: {state}
<button onClick={() => dispatch('increment')}>
Increment
</button>
<button onClick={() => dispatch('decrement')}>
Decrement
</button>
</>
);
}

export default Counter;

In the example above, we define our initial state (0) and a reducer function, which handles the ‘increment’ and ‘decrement’ actions. The useReducer hook is called with the reducer function and the initial state as arguments, giving us the current state value and a dispatch function in return.

The dispatch function is used inside the onClick handlers to dispatch the respective actions. When an action is dispatched, the reducer runs with the current state and the dispatched action, resulting in a new state.

useCallback hook

The useCallback hook is used to memoize a callback function. It helps in optimizing performance by preventing unnecessary re-rendering of components that depend on the callback.

Here is an example to explain useCallback:

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

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

// Callback function without useCallback
const handleClick = () => {
console.log("Button clicked!");
setCount(count + 1);
};

// Memoized callback function using useCallback
const memoizedHandleClick = useCallback(() => {
console.log("Button clicked!");
setCount(count + 1);
}, [count]);

return (
<div>
<p>Count: {count}</p>
{/* Using the handleClick without useCallback */}
<button onClick={handleClick}>Click me</button>
<br />
{/* Using the memoizedHandleClick with useCallback */}
<button onClick={memoizedHandleClick}>Click me (memoized)</button>
</div>
);
};

export default ExampleComponent;

In the above example, we have a component ExampleComponent that displays a count. There is a button that increments the count when clicked.

In the code without useCallback, a new instance of handleClick is created on every render regardless of whether the count has changed or not. This can be inefficient when passing the callback down to child components, as it may trigger unnecessary re-renders.

To optimize this, we can use useCallback to memoize the callback function. By passing an array of dependencies to useCallback, we can specify which variables should trigger a new instance of the callback function. In this case, we only want the callback to change if the count is updated.

With useCallback, the callback function is only recreated when the specified dependencies change. This ensures that the same callback instance is used unless its dependencies change. In the example, the memoizedHandleClick callback will only be updated if the count changes.

Using useCallback can be beneficial in scenarios where callbacks are passed as props to child components or used in the dependencies of other hooks, such as useEffect. It helps in preventing unnecessary re-renders and optimizing the performance of the application.

useMemo hook

The useMemo hook is used to memoize a value in React. It is helpful when you have a costly computation that should only be performed when necessary.

Here is an example to explain useMemo:

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

const ExampleComponent = () => {
const [number, setNumber] = useState(0);

const expensiveComputation = useMemo(() => {
console.log("Performing expensive computation...");
let result = 0;
for (let i = 0; i < number; i++) {
result += i;
}
return result;
}, [number]);

return (
<div>
<p>Number: {number}</p>
<p>Expensive Computation Result: {expensiveComputation}</p>
<button onClick={() => setNumber(number + 1)}>Increment</button>
</div>
);
};

export default ExampleComponent;

In the above example, we have a component ExampleComponent that displays a number and the result of an expensive computation. The computation is performed in the expensiveComputation variable using the useMemo hook.

The first parameter of useMemo is a function that performs the expensive computation. The return value of this function will be memoized. The second parameter is an array of dependencies that is used to determine if the memoized value needs to be updated. If any of the dependencies change, the memoized value is recalculated.

In the example, the expensive computation is only performed when the number changes. The result is memoized and will not be recalculated on subsequent renders if the number has not changed. This helps in optimizing the performance of the component by avoiding unnecessary computations.

useMemo is useful when you have expensive calculations, complex data transformations, or any value that is derived from other dependencies. It ensures that the computation is only performed when necessary, improving the efficiency of your application.

useRef hook

The useRef hook is a built-in hook in React that allows you to create a mutable value that persists across renders of a functional component. It provides a way to access and interact with DOM elements or any other value that is expected to persist throughout the component’s lifecycle.

Here’s an example to help demonstrate the use of useRef:

import React, { useRef } from 'react';

const ExampleComponent = () => {
const inputRef = useRef();

const handleClick = () => {
inputRef.current.focus();
};

return (
<div>
<input ref={inputRef} type="text" />
<button onClick={handleClick}>Focus on Input</button>
</div>
);
};

export default ExampleComponent;

In this example, the useRef hook is being used to create a reference to the <input> element. inputRef is a mutable value that persists across renders of the component. By passing inputRef as a value to the ref prop of the <input> element, we can access and manipulate this specific DOM node.

The handleClick function is triggered when the button is clicked. It utilizes the current property of the inputRef to access the actual DOM node of the input. In this case, inputRef.current.focus() is called to focus on the input element when the button is clicked.

By using the useRef hook, we are able to store a reference to the input element and access it whenever needed, without needing to re-render the component. This is useful for scenarios like focusing on an input field, triggering animations, or interacting with any other DOM element.

useLayoutEffect hook

The useLayoutEffect hook in React is similar to the useEffect hook, but it runs synchronously after the DOM has been updated but before the browser has finished painting the screen. It is useful for any operations that need to be performed synchronously after the render, such as measuring DOM elements or performing layout calculations.

The syntax of the useLayoutEffect hook is the same as useEffect, and it takes two arguments: a callback function and an optional array of dependencies. The behavior is also similar to useEffect, but the difference lies in the timing of when the callback is executed.

Here’s an example to demonstrate the use of useLayoutEffect:

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

const ExampleComponent = () => {
const divRef = useRef();

useLayoutEffect(() => {
// The callback function will run after the render but before the browser paints
// This can be used to perform synchronous DOM manipulations or measurements

const divElement = divRef.current;

// Perform some synchronous DOM manipulation or measurement
const width = divElement.offsetWidth;
const height = divElement.offsetHeight;

console.log(`Width: ${width}px, Height: ${height}px`);
});

return <div ref={divRef}>Example Component</div>;
};

In this example, we have a functional component ExampleComponent that renders a <div> element. We use the useLayoutEffect hook to perform synchronous DOM manipulations or measurements. Inside the callback, we access the <div> element using the divRef reference, and then measure its width and height using offsetWidth and offsetHeight. We log the measurements to the console.

The useLayoutEffect hook is useful in situations where you need to immediately access or modify the DOM after the render but before the browser paints. However, it’s important to note that using useLayoutEffect can potentially degrade performance, so it should be used sparingly and only when necessary. In most cases, the useEffect hook is sufficient.

useDebugValue hook

The useDebugValue is a custom hook in React that is used to provide additional debug information about a custom hook. It is mainly used for displaying custom hook values in the React DevTools.

The syntax of the useDebugValue hook is as follows:

import { useDebugValue } from 'react';

function useCustomHook() {
// custom hook logic...

// Use the useDebugValue hook to provide a label and value for debugging
useDebugValue(value, formatLabel);
// value: the value to be displayed in the React DevTools
// formatLabel: a function that formats the label displayed next to the value in the React DevTools
}

Here’s an example that demonstrates the use of useDebugValue:

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

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

useEffect(() => {
const intervalId = setInterval(() => {
setCount((prevCount) => prevCount + 1);
}, 1000);

// Cleanup the interval on unmount
return () => clearInterval(intervalId);
}, []);

// Provide debug information using useDebugValue
useDebugValue(count, (value) => `Count: ${value}`);

return count;
}

function Counter() {
const count = useCount();

return <div>Count: {count}</div>;
}

export default Counter;

In this example, we have a custom hook called useCount that uses the useState and useEffect hooks to create a count value that increments every second. Inside the useCount hook, we use the useDebugValue hook to provide debug information about the count value.

The useDebugValue hook takes two arguments: the count value and a function that formats the label. In this case, we provide the count value and format it as "Count: {value}".

The component Counter then uses the useCount hook and renders the count value in a <div>. When inspecting the component in the React DevTools, the count value will be displayed as “Count: {value}”.

The useDebugValue hook is helpful for adding debug information to custom hooks, allowing you to have more useful information displayed in the React DevTools when developing and debugging your application.

useImperativeHandle hook

The useImperativeHandle hook is used in React to customize the instance value that is exposed to parent components when using forwardRef. It allows you to define functions or values on a ref object, making them accessible from parent components through the ref attribute.

The syntax of the useImperativeHandle hook is as follows:

import { useImperativeHandle, forwardRef } from 'react';

const CustomComponent = forwardRef((props, ref) => {
// Define a mutable value using useRef or useState
const internalRef = useRef();

// Use the useImperativeHandle hook to define functions or values to expose
useImperativeHandle(ref, () => ({
// Define functions or values to expose
functionName: () => {
// Function logic
},
// ...
}));

// Component logic...

return <div>Custom Component</div>;
});

Here’s an example that demonstrates the use of useImperativeHandle:

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

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

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

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

const ParentComponent = () => {
const inputRef = useRef();

const handleClick = () => {
inputRef.current.focus();
};

const handleGetValue = () => {
console.log(inputRef.current.getValue());
};

return (
<div>
<FancyInput ref={inputRef} />
<button onClick={handleClick}>Focus Input</button>
<button onClick={handleGetValue}>Get Value</button>
</div>
);
};

export default ParentComponent;

In this example, we have a FancyInput component that wraps an HTML input element. Inside FancyInput, we define a inputRef using the useRef hook to store a reference to the actual input element.

We then use the useImperativeHandle hook to customize the instance value of the ref that is exposed to parent components. We define two functions, focus and getValue, that will be accessible from the parent component when using the

Thank you for reading!

--

--