React hooks let you use state and other React features without writing class components. Since their introduction in React 16.8, they have become the standard way to write functional components.
Using hooks offers several advantages:
- They let you manage state and lifecycle features inside functional components, making your code cleaner and more concise. You no longer need to worry about binding event handlers or translating lifecycle methods to classes.
- They make it easy to extract stateful logic from a component and reuse it elsewhere, which leads to highly modular and testable code.
- They can help optimize performance. For instance, hooks like
useMemolet you cache expensive calculations so they only run when their inputs change.
Modern React applications rely heavily on hooks. Here are some of the most common hooks you will work with:
💡 Want a quick reference? Check out our complete, interactive React Hooks Cheatsheet with comprehensive usage guidelines, examples, and rules for all built-in React hooks.
1. useState()
useState() is the go-to hook for adding state to functional components. It returns an array with two elements: the current state value and a function to update it.
import { useState } from "react";
function Example() {
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, the count variable holds the current state, and setCount updates it. When you click the button, setCount runs, the state updates, and the component re-renders to show the new value.
2. useEffect()
useEffect() lets you perform side effects in functional components. It covers the same use cases as componentDidMount, componentDidUpdate, and componentWillUnmount in class components.
import { useEffect, useState } from "react";
function Example() {
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>
);
}
Here, the useEffect() hook runs after every render, updating the browser document title with the latest click count.
3. useContext()
useContext() lets you read the value of a React context directly from a functional component.
import { useContext } from "react";
const ThemeContext = React.createContext("light");
function App() {
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}
function Toolbar() {
const theme = useContext(ThemeContext);
return (
<div>
<ThemedButton theme={theme} />
</div>
);
}
function ThemedButton(props) {
const style = { background: props.theme };
return <button style={style}>I am styled by theme context!</button>;
}
4. useLayoutEffect()
useLayoutEffect() is similar to useEffect(), but it runs synchronously right after the DOM has been updated, before the browser has a chance to paint. This is useful when you need to read the layout of the DOM, such as measuring the width or height of an element, to prevent visual flickers.
import { useLayoutEffect, useRef } from "react";
function MyComponent() {
const divRef = useRef(null);
useLayoutEffect(() => {
console.log(divRef.current.offsetWidth);
});
return <div ref={divRef}>Hello, world!</div>;
}
In this example, useLayoutEffect() logs the width of the div element synchronously. Because it runs before the browser repaints the screen, any updates you make based on that measurement will render seamlessly.
5. useReducer()
useReducer() is an alternative to useState() for managing complex state logic, especially when the next state depends on the previous one, or when you have multiple sub-values. It uses a reducer function similar to what you might find in Redux.
import { useReducer } from "react";
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 MyComponent() {
const [state, dispatch] = useReducer(reducer, { count: 0 });
return (
<div>
<p>The count is {state.count}</p>
<button onClick={() => dispatch({ type: "increment" })}>Increment</button>
<button onClick={() => dispatch({ type: "decrement" })}>Decrement</button>
</div>
);
}
The hook accepts a reducer function and an initial state, returning the current state and a dispatch function. You can call dispatch with an action object to trigger state updates.