React Hooks: State and Effects Guide

21 / Oct / 2023 by Mansi Teharia 0 comments

React Hooks were introduced in version 16.8 to revolutionize how state and other React features are handled in functional components, eliminating the need for class components. They provide a clean and intuitive way to manage state, side effects, and access key React functionalities. This article will delve into the fundamental hooks in React and their practical applications.

State Hooks

The useState hook allows functional components to incorporate state. It returns an array with two elements: the current state value and a function that allows you to update it.

import React, { useState } from "react";

// Functional component named Counter
function Counter() {
  // useState hook to add state to functional component
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}

export default Counter;

useReducer is an alternative to useState, managing complex state logic. It takes a reducer function and an initial state.

import React, { useReducer } from 'react';

// Initial state for the counter
const initialState = { count: 0 };

// Reducer function for managing state updates
function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 }; // Increase count by 1
    case 'decrement':
      return { count: state.count - 1 }; // Decrease count by 1
    default:
      throw new Error('Unsupported action type'); // Throw an error for unsupported actions
  }
}

// Functional component named Counter
function Counter() {
  // useReducer hook to manage state with a reducer function
  const [state, dispatch] = useReducer(reducer, initialState);

  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;

Effect Hooks

useEffect combines the functionalities of componentDidMountcomponentDidUpdate, and componentWillUnmount into a single hook. It allows you to perform side effects in function components.

import React, { useState, useEffect } from 'react';

// Functional component named DataFetcher
function DataFetcher() {
  const [data, setData] = useState(null);

  // useEffect hook for fetching data
  useEffect(() => {
    // Fetch data from an API
    fetch('https://api.example.com/data')
      .then((response) => response.json()) // Parse response as JSON
      .then((data) => setData(data)); // Update 'data' state with fetched data
  }, []); // Empty dependency array means it only runs on mount

  // Display the message from 'data', or 'Loading...' if 'data' is null
  return <p>{data ? data.message : 'Loading...'}</p>;
}

export default DataFetcher;

Dependency Array

The second argument of useEffect is an array of dependencies. It determines when the effect will be re-run:

1. The empty array means that the fetchData function will only be called on the initial render, and not on subsequent renders when the data state changes.

2. If it contains dependencies, the effect will only run if any of the dependencies have changed since the last render.

useLayoutEffect is a synchronous version of useEffect specifically designed for DOM mutations.

import React, { useState, useLayoutEffect } from "react";

function ExampleComponent() {
  const [width, setWidth] = useState(0);

  // useLayoutEffect hook for measuring element width
  useLayoutEffect(() => {
    // Get the width of the element with the ID 'myElement'
    const newWidth = document.getElementById("myElement").offsetWidth;
    setWidth(newWidth);
  }, []); // Empty dependency array means it only runs on mount

  return (
    <div>
      <div id="myElement">Resizable Element</div>
      <p>Width: {width}px</p>
    </div>
  );
}

export default ExampleComponent;

Additional Hooks

The useContext hook enables access to the value a React context provides in a functional component, eliminating the need for prop drilling.

import React, { useContext, createContext } from "react";

// Step 1: Create a Context
const MyContext = createContext();

// Step 2: Create a Provider Component
function MyProvider({ children }) {
  const sharedValue = "Hello from Context!";

  // Provide the shared value to the children
  return <MyContext.Provider value={sharedValue}>{children}</MyContext.Provider>;
}

// Step 3: Consume the Context with useContext
function ChildComponent() {
  // Consume the value provided by MyContext
  const contextValue = useContext(MyContext);

  // Display the context value
  return <div>{contextValue}</div>;
}

// Step 4: Use the Provider to wrap the component tree
function App() {
  return (
    <MyProvider>
      <ChildComponent />
    </MyProvider>
  );
}

export default App;
  • Additionally, you can explore this detailed article on simplifying context consumption with useContext. Click here

Additional Hooks

useRef return a mutable ref object that persists between renders.

import React, { useRef, useEffect } from 'react';

function TextInput() {
  // Create a ref to hold the input element
  const inputRef = useRef(null);

  // useEffect to focus on the input element after component mounts
  useEffect(() => {
    // Focus on the input element
    inputRef.current.focus();
  }, []);

  // Return an input element with the ref assigned
  return <input ref={inputRef} type="text" />;
}

export default TextInput;

useMemo memoizes function results based on dependencies.

import React, { useMemo } from 'react';

function ExpensiveCalculation({ data }) {
  // Use useMemo to memoize the result of the calculation
  const result = useMemo(() => {
    // Perform complex calculations using data
    // Return the result
    // Note: The actual calculations should be placed here
  }, [data]); // The dependency array ensures recalculation if 'data' changes

  return <p>{result}</p>;
}

useCallback memorizes a callback function. The main difference between useCallback and useMemo is the type of value they return. useCallback returns a memorized callback function, while useMemo returns a memorized value.

import React, { useCallback } from 'react';

function ParentComponent() {
  // Define a memoized callback function using useCallback
  const handleClick = useCallback(() => {
    // Handle click event
    // Add your click event handling logic here
  }, []); // Empty dependency array means the callback doesn't depend on any external variables

  // Render the ChildComponent and pass the memoized callback as a prop
  return <ChildComponent onClick={handleClick} />;
}

export default ParentComponent;

By utilizing these hooks, you can significantly enhance the functionality and readability of your React functional components, making them more efficient and easier to manage. Incorporating hooks into your React applications empowers you to build robust and dynamic user interfaces with less boilerplate code.

FOUND THIS USEFUL? SHARE IT

Leave a Reply

Your email address will not be published. Required fields are marked *