Hooks are functions that let us “hook into” state and lifecycle functionality in function components.
Hooks allow us to:
this keywordThere are two main rules to keep in mind when using hooks:
Common mistakes to avoid are calling hooks inside of loops, conditions, or nested functions.
// Instead of confusing React with code like this:if (userName !== '') {useEffect(() => {localStorage.setItem('savedUserName', userName);});}// We can accomplish the same goal, while consistently calling our hook every time:useEffect(() => {if (userName !== '') {localStorage.setItem('savedUserName', userName);}});
The primary purpose of a React component is to return some JSX to be rendered. Often, it is helpful for a component to execute some code that performs side effects in addition to rendering JSX.
In function components, we manage side effects with the Effect Hook. Some common side effects include: fetching data from a server, subscribing to a data stream, logging values to the console, interval timers, and directly interacting with the DOM.
The useEffect hook runs side effects after a render, with the frequency controlled by its dependency array. It takes two arguments in the form useEffect(callback, dependencies). The callback contains the side‑effect logic and runs after the initial render and again whenever one of the dependencies changes (or after every render if you omit the dependency array).
import React, { useState, useEffect } from 'react';function TitleCount() {const [count, setCount] = useState(0);useEffect(() => {document.title = `You clicked ${count} times`;}, [count]);return <button onClick={() => setCount(prev => prev + 1)}>+</button>;}
The cleanup function is optionally returned by the first argument of the Effect Hook.
If the effect does anything that needs to be cleaned up to prevent memory leaks, then the effect returns a cleanup function. The cleanup function is called before re-running the effect (when dependencies change) and when the component unmounts.
useEffect(() => {document.addEventListener('keydown', handleKeydown);//Clean up the effect:return () => document.removeEventListener('keydown', handleKeydown);});
useEffect() may be called more than once in a component. This gives us the freedom to individually configure our dependency arrays, separate concerns, and organize the code.
function App(props) {const [title, setTitle] = useState('');useEffect(() => {document.title = title;}, [title]);const [time, setTime] = useState(0);useEffect(() => {const intervalId = setInterval(() => setTime((prev) => prev + 1), 1000);return () => clearInterval(intervalId);}, []);// ...}
The dependency array is used to tell the useEffect() method when to call the effect.
useEffect(() => {alert('called after every render');});useEffect(() => {alert('called after first render');}, []);useEffect(() => {alert('called when value of `endpoint` or `id` changes');}, [endpoint, id]);
The useState() Hook lets you add React state to function components. It should be called at the top level of a React function definition to manage its state.
initialState is an optional value that can be used to set the value of currentState for the first render. The stateSetter function is used to update the value of currentState and rerender our component with the next state value.
const [currentState, stateSetter] = useState(initialState);
When the previous state value is used to calculate the next state value, pass a function to the state setter. This function accepts the previous value as an argument and returns an updated value.
If the previous state is not used to compute the next state, just pass the next state value as the argument for the state setter.
Note: The Reset button doesn’t need the function form since it’s not based on the previous state; it’s setting a fixed value.
function Counter({ initialCount }) {const [count, setCount] = useState(initialCount);return (<div>Count: {count}<button onClick={() => setCount(initialCount)}>Reset</button><button onClick={() => setCount((prevCount) => prevCount - 1)}>-</button><button onClick={() => setCount((prevCount) => prevCount + 1)}>+</button></div>);}
useState() may be called more than once in a component. This gives us the freedom to separate concerns, simplify our state setter logic, and organize our code in whatever way makes the most sense to us!
function App() {const [sport, setSport] = useState('basketball');const [points, setPoints] = useState(31);const [hobbies, setHobbies] = useState([]);}