React Hooks
React has introduced a feature called React Hooks from React 16.8 version. This feature allows functional components to have state and lifecycle management without using class components.
Let’s learn more about it and learn how to do state management in functional components using React Hooks feature.
What is React Hooks
React Hooks is a feature that allows you to use features like State, Lifecycle methods, etc in functional components. Basically, React Hooks are special functions that start with the prefix “use” (for example: `useState`, `useEffect`, `useRef`, etc.) and it provides way to use reuse stateful logic across the components without using of class components and
higher-order components (HOCs).
React provides us some built-in Hooks and also allows us to create custom Hooks as well. So first we will learn about built-in Hooks.
Here are the list of built-in Hooks in React:
- useState
- useEffect
- useRef
- useMemo
- useReducer
- useContext
- useCallback
- useLayoutEffect
- useImperativeHandle
- useDebugValue
How to use React Hooks
Let’s learn how to use each react hook one by one.
useState Hook
The useState Hook allows you to add and track state in functional components. It returns a state variable and a function to update that state.
Let’s understand the basic syntax of useState Hook. The useState syntax looks like this:
const [stateVariable, setState] = useState(initialValue);
In the basic syntax of useState, here the `stateVariable` is a state variable that holds the current state value.
`setState` is a function used to update the state variable. It takes a new value as an argument and triggers a re-render with the updated state.
`initialValue` is the initial value of the state variable. It is only used during the first render of the component.
Let’s see How to use useState Hook in a functional component by creating a counter: First import the useState Hook in your functional component. Like this:
import React, {useState} from "react";
Use the useState hook to declare a state variable and its setter function. Like this:
Here count is a state variable, setCount is a function to update the state value. These names are like variables, so you can name them anything as you want.
Here `0` is the initial state and the initial state is stored in the count variable. I render the count variable as a JavaScript expression in the JSX element (h2). Output will be: 0
import React, {useState} from "react"; function App() { const [count, setCount] = useState(0); return ( <div className="App"> <h2>{count}</h2> </div> ); } export default App;
Then I have created an event handler function called `handleClick`. Inside this function I have called `setCount(count + 1)`, and added this event handler function to the button JSX element.
So, when the button is clicked by the user, the count state will keep updating and incrementing, and 1 will be added to count value every time the button is clicked.
import React, {useState} from "react"; function App() { const [count, setCount] = useState(0); const handleClick = () => { setCount(count + 1); }; return ( <div className="App"> <h2>{count}</h2> <button onClick={handleClick}>Click Me!</button> </div> ); } export default App;
Final Output: 1, 2, 3, 4, 5, 6, …
useEffect Hook
The useEffect Hook allows you to perform side effects in your functional components. Side effects in React refer to any action that affects the outside world, such as data fetching, directly updating the DOM, setting up event listeners, etc.
useEffect accepts two arguments, the second one is optional, but in some cases you need to use it.
useEffect syntax:
useEffect(() => { }, [])
In the useEffect syntax, the two arguments are function and dependency array.
Example of `useEffect` Hook in a functional component
First import the useEffect Hook and useState Hook in your functional component.
Example 1:
import { useState, useEffect } from "react"; function Counter() { const [count, setCount] = useState(0); useEffect(() => { setTimeout(() => { setCount((count) => count + 1); }, 1000); }); return <h1>I have rendered {count} times!</h1>; } export default Counter;
In the above code example, I have created a simple counter. The useEffect hook is used to handle side effects in this component. In this case, it sets up a timer using setTimeout. After a delay of 1000 milliseconds (1 second), it updates the count state by incrementing its current value using the setCount function. It runs on every render.
As you noted the setCount function is used in a callback to ensure that the update is based on the latest value of count.
Example 2:
import { useState, useEffect } from "react"; function Counter() { const [count, setCount] = useState(0); useEffect(() => { setTimeout(() => { setCount((count) => count + 1); }, 1000); }, []); return <h1>I have rendered {count} times!</h1>; } export default Counter;
In the second example, after adding the empty dependency array `[]` passed as the second argument to useEffect indicates that the effect should only run once on the first render, specifically after the component mounts.
Example 3:
function Counter() { const [count, setCount] = useState(0); const [data, setData] = useState(''); const handleChange = (e) => { setData(e.target.value) } useEffect(() => { setCount((count) => count + 1); }, [data]); return ( <> <input onChange={handleChange} value={data} /> <p>{count}</p> </> ); } export default Counter;
In the third example, the handleChange function is used as an event handler for the `onChange` event of the input element. It updates the data state with the value of the input field whenever the user types.
Then, useEffect Hook updates the count state by incrementing its value by 1 whenever the data state changes.
The dependency array [data] indicates that the effect will run whenever the data state changes.
Must know: You can also select DOM elements using useEffect Hook. Like this:
useEffect(() => { const card = document.querySelector(".container"); const body = document.body; })
useRef Hook
In React useRef Hook allows you to create a mutable reference to a DOM element or any other value. It’s a way to access and interact with DOM elements directly, without triggering a re-render when the reference changes.
Accessing DOM elements with using useRef Hook
import React, { useRef, useEffect } from 'react'; function MyComponent() { // Creating a ref to store a DOM element reference const myInputRef = useRef(null); // Using useEffect to focus the input when the component mounts useEffect(() => { if (myInputRef.current) { myInputRef.current.focus(); } }, []); return ( <div> <input type='text' ref={myInputRef} /> </div> ); } export default MyComponent;
In this example, we’re creating a ref using the useRef hook and assigning it to the ref attribute of the input element. We’re also using the useEffect hook to focus the input when the component mounts.
Notice that we’re accessing the DOM element using the `.current` property of the `ref` (myInputRef.current).
useContext Hook
React Context is a way to manage state globally. Like, context provides a way to share data that can be considered “global” means you access the context of a parent component from a child component, without having to pass props through every level of the component tree.
By this Hook data can be shared across multiple components in a more efficient and convenient manner.
Example of useContext Hook
First we have to create a context, so create `ThemeContext.js` file in the `src` folder.
import { createContext } from "react"; const ThemeContext = createContext(); export default ThemeContext;
Then here import the createContest Hook to create context.
After creating ThemeContext import it to your Root component (App.js).
Here the root component provides the context data to its child components using the `ThemeContext.Provider`. The context value being provided as `themeState`, which value is set to the ‘dark’.
import "./App.css"; import Header from "./components/Header"; import ThemeContext from "./ThemeContext"; function App() { const themeState = 'dark'; return ( <ThemeContext.Provider value={themeState}> <div className="App"> <Header/> </div> </ThemeContext.Provider> ); } export default App;
The value `prop` is set to themeState, which means that any child component inside the
Provider can access this value using the `useContext` hook. Now we can access this value in the Header component.
import React, {useContext} from 'react' import ThemeContext from './ThemeContext'; function Header() { const themeState = useContext(ThemeContext); return ( <header className={themeState}></header> ) } export default Header;
As you can see in the above code of Header component, I have imported the `useContext` Hook which is used to consume the context data from ThemeContext. The `themeState` variable now holds the value provided by the `ThemeContext.Provider` in the parent component.
useReducer Hook
The useReducer hook is used for managing more complex state logic in a component. It’s similar to useState Hook. It allows you to write custom state logic.
when you have complex state logic, changing more than 2-3 states in a function, then you have to use useReducer Hook.
Syntax of useReducer Hook
const [state, dispatch] = useReducer(reducer, initialState);
Example of using useReducer Hook:
To use useReducer Hook it obviously that we have to import it first.
import React, { useReducer } from 'react'; // Reducer function: Takes current state and an action, returns new state const reducer = (state, action) => { switch (action.type) { case 'INCREMENT': return { count: state.count + 1 }; case 'DECREMENT': return { count: state.count - 1 }; default: return state; } }; function MyComponent() { // Initial state object const initialState = { count: 0 }; // useReducer returns the current state and a dispatch function to update state const [state, dispatch] = useReducer(reducer, initialState); return ( <div> <p>Count: {state.count}</p> <button onClick={() => dispatch({ type: 'DECREMENT' })}>Decrement</button> <button onClick={() => dispatch({ type: 'INCREMENT' })}>Increment</button> </div> ); } export default MyComponent;
Step 1:
As you can see in the above code example, I have created a function called reducer.
Here the reducer function is defined outside the component. This function takes two parameters: the current state and an action object. It uses a switch statement to determine how the state should be updated based on the action type.
In this case, it handles two types of actions: ‘INCREMENT’ and ‘DECREMENT’, updating the count property accordingly.
Step 2:
Inside the MyComponent function, an initial state object named `initialState` is defined. Initially the`count` value is set to 0.
Step 3:
Here the useReducer hook takes two arguments: the reducer function and the initialState object.
It returns two values: the state object representing the current state, and the dispatch function which is used to send actions to the reducer.
Step 4:
In JSX, I have added two button elements. One button is labeled “Decrement” and it has an
`onClick` event handler that dispatches an action of type ‘DECREMENT’ to the reducer. The second button is labeled “Increment” and it dispatches an action of type ‘INCREMENT’.
useCallback Hook
The useCallback Hook used to memoize functions in order to optimize performance, especially in scenarios where the function references are passed down to child components as props.
It helps in preventing unnecessary re-creation of functions on every render, which can lead to improved performance and reduced unnecessary re-renders.
Example: MyComponent.js
import React, { useState, useCallback } from 'react'; import SecondComponent from './SecondComponent'; function MyComponent() { const [count, setCount] = useState(0); // Using useCallback to memoize the function const handleClick = useCallback(() => { console.log("Button clicked"); }, []); return ( <div> <p>Count: {count}</p> <button onClick={() => setCount(count + 1)}>Increment</button> <SecondComponent handleClick={handleClick}/> </div> ); } export default MyComponent;
SecondComponent.js
import React from 'react' function SecondComponent({handleClick}) { return ( <div> <button onClick={handleClick}>Click me</button> </div> ) } export default SecondComponent;
The useCallback and useMemo Hooks are similar. The main difference is that useMemo returns a memoized value and useCallback returns a memoized function.
useMemo Hook
useMemo Hook returns a memoized value that can be particularly helpful for optimizing performance when dealing with expensive calculations or computations that don’t need to be re-evaluated on every render.
import React, { useState, useMemo } from 'react'; function MyComponent() { const [count, setCount] = useState(0); const doubledCount = useMemo(() => { return count * 2; }, [count]); return ( <div> <p>Count: {count}</p> <p>Doubled Count: {doubledCount}</p> <button onClick={() => setCount(count + 1)}>Increment</button> </div> ); } export default MyComponent;
Here in the above example, the useMemo hook is used to memoize the result of a computation.
In this case, the computation is simply doubling the value of the `count` state. The function inside useMemo returns the computed value, count * 2.
The dependency array [count] specifies that the computation should be re-evaluated whenever the `count` state changes.