Skip to Content

React Hooks: A Comprehensive Guide with Examples

React Hooks allow developers to use state and other React features inside functional components, providing a more concise and readable way to manage component logic and state compared to class-based components.

In this tutorial, we'll cover the basics of React Hooks and show you how to use them in your React applications.

Prerequisites

Before you start with React Hooks, make sure you have the following:

Introduction to React Hooks

React Hooks are functions that allow you to "hook into" React state and lifecycle features from functional components. They were introduced in React 16.8 to address the following issues:

  • Reusing stateful logic between components (previously, this required using higher-order components order render props).
  • Complex component logic in class components made the code harder to read and maintain.

React provides a set of built-in hooks and allows you to create custom ones. These hooks can be imported from the react library.

Basic Hooks

useState

The useState hook allows you 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>Count: {count}</p>
<button onclick={() => setCount(count + 1)}>Increment</button>
</div>
);
}

export default Counter;

useEffect

The useEffect hook enables you to perform side effects in your components, such as data fetching, DOM manipulation, or subscribing to a data source. It takes two arguments: a function to run the side effect and an array of dependencies that trigger the effect when any of them change.

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

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

useEffect(() => {
fetch("https://api.domain.com/data")
.then((response) => response.json())
.then((data) => setData(data));
}, []);

return (
<ul>
{data.map((item) => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
}

export default Fetcher;

useContext

The useContext hook allows you to access the context values provided by a Context.Provider higher up in the component tree.

import React, { useContext } from "react";

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

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

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

export default ThemedComponent;

Additional Hooks

useRef

The useRef hook allows you to create mutable object references that persist across renders. This can be useful for accessing and modifying DOM elements directly.

import React, { useRef, useEffect } from "react";

function FocusInput() {
const inputRef = useRef(null);

useEffect(() => {
inputRef.current.focus();
}, []);

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

export default FocusInput;

useReducer

The useReducer hook is an alternative to useState for managing complex state logic. It takes a reducer function and an initial state.

import React, { useReducer } from "react";

function counterReducer(state, action) {
switch (action.type) {
case "INCREMENT":
return { count: state.count + 1 };
case "DECREMENT":
return { count: state.count - 1 };
default;
return state;
}
}

function Counter() {
const [state, dispatch] = useReducer(counterReducer, { count: 0 });

return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: "INCREMENT" })}>Increment</button>
<button onClick={() => dispatch({ type: "DECREMENT" })}>Decrement</button>
</div>
);
}

export default Counter;

useCallback and useMemo

useCallback and useMemo are used for optimizing performance by memoizing values. useCallback memoizes functions, and useMemo memoizes values.

import React, { useState, useMemo, useCallback } from "react";

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

const calculation = useMemo(() => {
return count * 2;
}, [count]);

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

return (
<div>
<p>Result of calculation: {calculation}</p>
<button onClick={clickHandler}>Increment Count</button>
</div>
);
}

export default Calculation;

Custom Hooks

You can create your own custom hooks to encapsulate and reuse component logic across different components.

import { useState, useEffect } from "react";

function Fetcher(url) {
const [data, setData] = useState([]);

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

return data;
}

export default Fetcher;

Rules of Using Hooks

When working with React Hooks, you should follow these rules:

  • Only call hooks at the top level of your functional component or custom hook.
  • Only call hooks from within functional components or custom hooks, not regular JavaScript functions.
  • Ensure the order of the hook calls is consistent across renders.

Common Hook Patterns

Some common patterns you might encounter when using hooks include managing form state, handling authentication, and using context for global state management. Each of these patterns can be achieved by creating custom hooks tailored to your specific needs.

Conclusion

React Hooks provide a more elegant and functional way to manage state and side effects in React applications. By using basic hooks like useState and useEffect, along with custom hooks, you can create cleaner, more maintainable code for your React components.

As you become more familiar with hooks, you'll find that they simplify complex component logic and make it easier to build and maintain React applications.

Created: September 27, 2023