Exploring Advanced React hooks

React Hooks are functions that enable you to utilize state and other features of React without the need to write class components.

April 12, 20245 min read

Introduction

Hooks are functions that let you “hook into” React state and lifecycle features from function components. They were introduced in React 16.8 version. Hooks allow you to use state and other React features without writing a class. Hooks provide a more concise and elegant way to manage stateful logic and side effects within your components. This makes your code clean and easier to maintain.

Rules of React hooks

  1. Hooks can only be called inside the body of a function component.
  2. Hooks should be called at the top level in the body of a function component and a custom hook.
  3. Hooks should not be called in event handlers.
  4. Hooks should not be called after the return statement.

Try Kodaschool for free

Click below to sign up and get access to free web, android and iOs challenges.

Sign Up

Let’s explore React Hooks.

React offers different ways of using hooks, allowing developers to use them based on the application’s needs. This approach enhances code reusability and readability, streamlining the development process for complex applications.

useState hook

useState is a React Hook that lets you add a state variable to your component. The useState returns an array that takes in two parameters.

  1. state — This is the initial value of the state that can be any type.
  2. setter function — The function is used to update state.

export default function Form() {
  // Initializing state as an object with properties 'name' and 'email'
  const [formState, setFormState] = useState({
    email: '',
    password:''
  });

  // Function to handle changes in the input fields
  const handleChange = (event) => {
    const { name, value } = event.target;
    setFormState(prevState => ({
      ...prevState,
      [name]: value
    }));
  };

  return (
    <form>
      <div>
        <label>Name:</label>
        <input
          type="email"
          name="email"
          value={formState.email}
          onChange={handleChange}
        />
      </div>
      <div>
        <label>Email:</label>
        <input
          type="password"
          name="password"
          value={formState.password}
          onChange={handleChange}
        />
      </div>
      <button type="submit">Submit</button>
    </form>
  );
}

useReducer hook

useReducer is a hook that is used to handle complex state manipulations and updates when certain actions are dispatched. When dealing with complex state, useReducer is preferred over useState.The useReducer hook takes in two parameters which are the reducer function and, the initial state. The value of the initial state could be anything depending on what you want to initialize.

import React, { useReducer } from 'react';

// Define the initial state of the form
const initialState = {
  name: '',
  email: ''
};

// Reducer function to handle state changes based on action type
function Form(state, action) {
  switch (action.type) {
    case 'SET_NAME':
      return { ...state, name: action.payload };
    case 'SET_AGE':
      return { ...state, age: action.payload };
    case 'SET_EMAIL':
      return { ...state, email: action.payload };
    default:
      return state;
  }
}

// Component
function FormComponent() {
  const [state, dispatch] = useReducer(formReducer, initialState);

  return (
    <div>
      <button onClick={() => dispatch({ type: 'SET_NAME', payload: 'John Doe'})}>name</button>
      <button onClick={() => dispatch({ type: 'SET_EMAIL', payload: 'johndoe@gmail.com'})}>email</button>
    </div>
  );
}

export default Form;

The above example is used for complex forms to handle multiple form fields in a structured way. The useReducer makes the state transitions and centralizes the state logic, making the component easier to manage.

useContext hook

useContext is a hook that lets you read and subscribe to context from your component. The useContext hook is used for managing and passing state throughout your component tree without having to explicitly pass props down through every component. This hook is part of React's Context API, which allows you to share values like user information across all levels of your application without prop drilling.

Creating context

Create a context object

import React, { createContext, useContext, useState } from 'react';

interface UserContextType {
  user: {
   name: string;
   age: number;
  };
  setUser: React.Dispatch<React.SetStateAction<{
    name: string;
    age: number;
}>>
}

// Create a Context
export const UserContext = createContext<UserContextType | null>(null);

Provider Component

The provider component supplies data to any component that needs it at any level of the app tree.

import React, { ReactNode } from "react";
const App = () => {
  return (
    <UserProvider>
      <div>
        <h1>Welcome to Our Application</h1>
        <UserProfile />
      </div>
    </UserProvider>
  );
};interface IProps{
 children: ReactNode
}
const UserProvider = ({ children }:IProps) => {
  const [user, setUser] = useState({ name: "John Doe", age: 30 });
  const value ={}
  return (
    <UserContext.Provider value={{ user, setUser }}>
      {children}
    </UserContext.Provider>
  );
};
export default UserProvider;

Consuming context

The useContext hook reads the context value accessed by any component.

import {UserContext} from  "./userContext.tsx"
const UserProfile = () => {
  const { user, setUser } = useContext(UserContext);
  const handleClick = () =>{
   setUser({ ...user, age: user.age + 1 })
 }

  return (
    <div>
      <h1>{user.name}</h1>
      <p>Age: {user.age}</p>
      <button onClick={handleClick}>
        Increase Age
      </button>
    </div>
  );
};
export default UserProfile;

App Component

The provider wraps the whole application to make the context available throughout the application at any level.

import UserProfile from "./UserProfile"
import UserProvider from "./UserProvider"
const App = () => {
  return (
    <UserProvider>
      <UserProfile />
    </UserProvider>
  );
};
export default App

useRef hook

The useRef Hook stores a mutable value that does not cause re-render when updated. It allows the persistence of values between renders. This hook makes it possible to access DOM nodes within a functional component.

import React, { useRef } from 'react';

function Input() {
  // Create a ref object
  const inputRef = useRef<HTMLInputElement>(null);

  const handleFocus = () => {
    // Use the `current` property to access the referenced DOM element
    inputRef.current?.focus();
  };

  return (
    <div>
      <input ref={inputRef} type="text" placeholder="Click button to focus" />
      <button onClick={handleFocus}>Focus the input</button>
    </div>
  );
}

export default Input;

useEffect hook

useEffect allows us to perform side effects on functional components. Side effects include data fetching, directly, manipulating the DOM, and subscribing to events such as resize and keyboard events.

The useEffect takes in two arguments, the function that contains the side effect and an array of dependencies. The useEffect runs whenever the array ode dependency changes. If the array of dependency is empty, the effect will only run once after the initial render.

useEffect for Data fetching.

import { useState, useEffect } from 'react';

function useFetch(url) {
  const [data, setData] = useState([]);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState('');
  const fetchData = async () => {
      try {
        setLoading(true);
        const response = await fetch(url);
        if (!response.ok) {
          throw new Error('Failed to fetch data');
        }
        const result = await response.json();
        setData(result);
        setError('');
      } catch (error:any) {
        setError(error.message);
      } finally {
        setLoading(false);
      }
    };
  useEffect(() => {
    fetchData();
  }, [url]);

  return { data, loading, error };
}

export default useFetch;

This custom hook encapsulates the useEffect to fetch data, making it reusable across multiple components. The data is fetched again when the component mounts or when the URL changes.

Mary Maina

About Mary Maina

I am a frontend devloper