React Hooks: A Comprehensive Guide with Examples
React Hooks make it easy to use state and other React features in functional components. They simplify managing component logic and state, offering a cleaner and more readable alternative to class-based components.
In this tutorial, we'll cover the basics of React Hooks and show you how to use them in your applications!
Prerequisites
Make sure you have the following:
- A basic knowledge and understanding of how React works.
- NodeJS and npm (Node Package Manager) are installed on your computer. If not, you can read more on creating a NodeJS with Express web server to get setup!
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. Before, you had to use higher-order components or render props to do this.
- Complex component logic in class components made the code harder to read and maintain.
React provides a set of built-in hooks that allows you to create custom ones that we'll go over now!
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, and subscribing to a data source. It takes two arguments: a function to run the side effect and an array of dependencies that trigger the side 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 React 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 your applications. By using basic hooks like useState
and useEffect
, along with custom hooks, you can create cleaner, more maintainable code for your React components.
Written by: Josh Rowe
Last Updated: January 06, 2025Created: September 27, 2023