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:
- 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.
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.