Hooks in React
Introduction to Hooks
React Hooks were introduced in React 16.8 to simplify how developers manage state and side effects in functional components. Before Hooks, functional components were stateless, and only class components could hold state or use lifecycle methods. This often led to complex class structures and repetitive code. Hooks solve this by allowing developers to use state and other React features directly in functional components, making the code cleaner and easier to manage.
Pros of React Hooks
- Simplify Code : With class components, developers had to deal with this, binding methods, and complex lifecycle methods. Hooks eliminate these issues by keeping everything in functional components, which are easier to write and maintain.
- Reuse Logic : Before Hooks, reusing logic across components was challenging. Developers had to rely on patterns like higher-order components (HOCs) or render props, which made the code harder to follow. Hooks, especially custom hooks, enable the reuse of logic more naturally, without modifying the component tree structure.
- Clean Code : Class components often resulted in code that mixed stateful logic with UI concerns. Hooks let developers separate different pieces of logic (e.g., managing form state, fetching data) into smaller, isolated functions, leading to better-organized code.
Rules of Hooks
Call Hooks Only at the Top Level
Hooks should only be called at the top level of your component, not inside loops, conditions, or nested functions. This ensures that Hooks are called in the same order on every render, which is crucial for React to correctly preserve the component’s state between renders.
Call Hooks Only in React Functions
Hooks should only be called inside functional components or custom hooks, not in regular JavaScript functions or class components. This is to ensure that React can properly manage the component’s state.
React Hooks fundamentally changed how developers write and manage functional components, making them more powerful and easier to understand. By adhering to the rules of hooks and leveraging them for state management, side effects, and reusable logic, developers can create cleaner, more maintainable code.
Basic Hooks
React provides several hooks that allow you to handle common tasks like managing state and handling side effects in functional components. Two of the most important and widely used hooks are useState and useEffect.
useState
: Managing State in Functional components
The useState hook is used to add state to functional components. Before hooks, only class components could have state. Now, with useState, any functional component can manage its own state.
The useState hook allows you to declare a state variable and a function to update that state. When you call useState, you pass the initial state as an argument, and it returns an array with two elements:
- The current state
- A function that updates the state
Syntax of useState
stateVariable
: This is the current value of the state.setStateVariable
: This is a function used to update the value of stateVariable.initialValue
: This is the starting value for the state.
Example of useState
Install the following libraries. Assuming you have installed
In this example:
We declare a state variable count
and a function setCount
to update it. useState(0)
initializes count to 0. When the button is clicked, setCount
is called with the new value (count + 1), which re-renders the component with the updated state.
Initializing state with Function
Sometimes, initializing state might involve a heavy calculation. You can pass a function to useState to compute the initial state only once when the component first renders.
State Updation
When updating state based on the current state (e.g., incrementing or toggling values), you can pass a function to setState. This ensures you’re working with the latest state value.
useEffect
: Handling Side effects
The useEffect
hook is used to handle side effects in functional components. Common side effects include data fetching, subscriptions, and directly interacting with the DOM. Before hooks, these kinds of side effects were managed using lifecycle methods like componentDidMount
, componentDidUpdate
, and componentWillUnmount
in class components. useEffect
combines all of these into one unified API. Basically, it takes two arguments
- A function where you define the side effect.
- An optional dependency array that controls when the effect should run.
Syntax of useEffect
Side effect function is the function that runs after each render, performing the desired effect (e.g., fetching data). Dependency is an optional array that specifies when the effect should re-run. If a value in the array changes, the effect will re-run.
Example of useEffect
Install the following libraries. Assuming you have installed
In this example: The side effect is fetching data from an API when the component first renders. The [] (empty dependency array) ensures the effect only runs once, similar to componentDidMount
.
Controlling using Dependency array
You can control when an effect runs by adding dependencies. The effect will only run again if one of the dependencies changes.
Cleaning up effects
Sometimes, side effects like subscriptions or timers need to be cleaned up to prevent memory leaks. useEffect
can return a cleanup function that runs when the component unmounts or before the effect re-runs.
useContext
: Accessing Global State with React’s Context API
The useContext hook is used to access values provided by the Context API in React. The Context API allows you to create and manage global state, which can be shared across different components without passing props down through every level of the component tree. This is particularly useful for managing themes, authentication status, or user settings that are needed by many components
useContext allows a component to consume the nearest Context Provider’s value without needing to pass props through intermediate components. This helps reduce “prop drilling” (passing props down multiple levels) and makes the code more readable.
Steps to use useContext
-
First, create a context using React.createContext(). This provides a default value for the context.
-
Wrap your component tree with a Context Provider to supply a value that child components can consume.
-
Use the useContext hook inside any component that needs the context value.
Example of useContext
Install the following libraries. Assuming you have installed
In this example, DisplayComponent
accesses the value “Hello, World!” from MyContext
using useContext
.
useRef
: Referencing DOM Elements and Persisting Values
The useRef
hook is used to reference DOM elements or persist values across renders without causing the component to re-render. Unlike state variables, changing a useRef
value does not trigger a re-render.
You can use useRef
to directly access a DOM element, such as focusing an input field or scrolling to a specific section. useRef
can store a mutable value that persists between renders, without triggering re-renders when the value changes.
Syntax of useRef
useRef
returns a mutable object that has a single property called current. The current property of the object can hold any value (similar to an instance variable in class components) and is preserved across renders.
Example of useRef
Install the following libraries. Assuming you have installed
useReducer
: Managing Complex State
The useReducer
hook is an alternative to useState
for managing state, particularly when the state logic is complex or the state has multiple sub-values that need updating in various ways. It is often used in large-scale applications or for cases where useState
would result in messy, hard-to-maintain code.
useReducer
is similar to the reduce
function in JavaScript. It takes a reducer function and an initial state as arguments and returns:
- The current state.
- A dispatch function to send actions to the reducer.
Syntax of useReducer
reducerFunction
: This is a function that takes the current state and an action, then returns the new state.initialState
: This is the initial value of the state.dispatch
: This function is used to send actions that update the state.
Example of useReducer
Install the following libraries. Assuming you have installed
In this example, useReducer manages simple increment and decrement actions for a counter.
useMemo
: Memoizing Expensive Calculations
The useMemo
hook in React is used to memoize expensive calculations, so they are only recomputed when their dependencies change, rather than on every render. This can help improve performance by avoiding unnecessary re-calculations, especially in cases where the calculation is resource-intensive or when working with large data sets. It actually takes two arguments:
- A function that returns the computed value.
- An array of dependencies.
Syntax of useMemo
- memoizedValue : The result of the computation.
- computeExpensiveValue : The expensive function that only runs when dependencies (a, b) change.
Example of useMemo
Install the following libraries. Assuming you have installed
In this example, expensiveCalculation
is only re-calculated when either a
or b
changes. If other parts of the component re-render without changing a
or b
, the expensive calculation won’t re-run. This reduces unnecessary computation and improves performance.
useCallback
: Memoizing Functions for Performance
The useCallback
hook is used to memoize functions so that they are not recreated on every render. This is particularly useful when passing functions as props to child components or as dependencies to other hooks (like useEffect
), as it prevents unnecessary re-renders. This actually takes two arguments.
- The function to memoize.
- An array of dependencies.
Syntax of useCallback
memoizedFunction
: A memoized version of the function.- Dependencies: If any of the values in the array change, the function is recreated.
Example of useCallback
Install the following libraries. Assuming you have installed
In this example,
- The Counter component manages a count state using
useState
, and it includes a child componentChildComponent
that takes anonClick
handler as a prop. - The
ChildComponent
renders a button and logs to the console every time it re-renders. - The
handleClick
function insideCounter
is wrapped withuseCallback
. This ensures that the function is memoized and won’t be recreated on every render unless its dependencies change (in this case, the dependency array is empty, so it won’t change). - Without
useCallback
, every render of Counter would create a new version ofhandleClick
, which would causeChildComponent
to re-render unnecessarily. UsinguseCallback
prevents this, ashandleClick
stays the same between renders.
Custom Hooks
In React, Custom Hooks allow you to extract and reuse logic across multiple components, making your code cleaner, more modular, and easier to maintain. Custom Hooks are JavaScript functions that can use any of React’s built-in hooks like useState
, useEffect
, useContext
, etc., and can return values or functions that encapsulate the shared logic.
When and Why to Use Custom Hooks
You should consider creating a Custom Hook when:
- You have logic that is reused across multiple components. For example, fetching data, handling forms, managing local storage, or handling timers.
- You want to keep your components cleaner and less cluttered. By moving logic to a custom hook, components can focus on UI-related tasks.
- You need to separate concerns. Custom Hooks allow you to organize your code by concern, making it easier to test, modify, and maintain.
Steps to create custom Hook
A Custom Hook is essentially a function that:
- Starts with the word use (to follow React’s convention and allow it to use hooks inside).
- Can call other hooks inside itself.
- Returns values (or functions) that the component can use.
Example of Custom Hook
Install the following libraries. Assuming you have installed
In this example, useCounter
manages the counter logic using useState
, providing three functions to manipulate the count: increment
, decrement
, and reset
. The hook returns the current count
and the functions, allowing easy access in any component that uses it. CounterComponent
uses the useCounter
hook to access the counter state and functions, keeping the component focused on rendering. Buttons in the component trigger the respective functions, allowing users to interact with the counter, with automatic UI updates reflecting the changes.