Reactjs custom hooks by examples

2023-03-03

Learn Reactjs custom hooks by examples

ReactJS custom hooks are functions that you can create and use in your React application. They are called "custom" hooks because you create them yourself, rather than using the built-in hooks provided by React (such as useState and useEffect).

The purpose of a custom hook is to encapsulate some behavior or logic that you want to reuse across multiple components in your application. For example, let's say you have a form that has several inputs, and you want to validate each input when the user submits the form. You could create a custom hook called useFormValidation that contains the validation logic, and then use that hook in each component that needs to validate a form.

The benefits of using a custom hook are that it can help you write cleaner, more modular code. By encapsulating complex logic in a separate function, you can make your components simpler and easier to understand. Additionally, because the custom hook can be reused across multiple components, you can avoid duplicating code and make your application more efficient.

I will try to explain the following 5 custom hooks:

useLocalStorage, useToggle, useIntersectionObserver, useOnClickOutside and useInterval

so lets begin

useLocalStorage

The localStorage API allows you to store key-value pairs in the browser's local storage. In React, you can use this API to implement features like persisting user preferences or keeping track of the user's login state.

To make React use the localStorage API, you can create a custom hook that uses the useState and useEffect hooks to read and write data to the localStorage.

Here's an example of how to create a useLocalStorage hook:

import { useState, useEffect } from "react";

function useLocalStorage(key, initialValue) {
  const [value, setValue] = useState(() => {
    const storedValue = localStorage.getItem(key);
    return storedValue !== null ? JSON.parse(storedValue) : initialValue;
  });

  useEffect(() => {
    localStorage.setItem(key, JSON.stringify(value));
  }, [key, value]);

  return [value, setValue];
}

In this hook, we define a state variable called value and a setValue function to update it. We use the useState hook to initialize value to the value stored in the localStorage for the given key, or to the initialValue if no value is found.

We also use the useEffect hook to update the localStorage whenever value changes. We pass an array of dependencies to the useEffect hook that includes key and value to ensure that the effect runs only when these values change.

Finally, we return an array containing value and setValue so that it can be used in the component that consumes it.

To use this hook in a component, you can import it and call it like this:

import React from "react";
import useLocalStorage from "./useLocalStorage";

function MyComponent() {
  const [count, setCount] = useLocalStorage("count", 0);

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

In this example, we create a state variable called count and a setCount function to update it. We use the useLocalStorage hook to initialize count to the value stored in the localStorage for the key 'count', or to 0 if no value is found.

We then render the current value of count and a button to increment it using setCount. When the button is clicked, the count state variable is updated and the new value is stored in the localStorage using the useEffect hook defined in the useLocalStorage hook.

useToggle

The useToggle hook is a simple custom hook that allows you to toggle a boolean value. In React, you can use this hook to implement features like expanding and collapsing sections, toggling a menu, or switching between two states.

Here's an example of how to create a useToggle hook:

import { useState, useCallback } from "react";

function useToggle(initialValue = false) {
  const [value, setValue] = useState(initialValue);

  const toggleValue = useCallback(() => {
    setValue((value) => !value);
  }, []);

  return [value, toggleValue];
}

In this hook, we define a state variable called value and a setValue function to update it. We use the useState hook to initialize value to initialValue or false if no value is provided.

We also define a toggleValue function using the useCallback hook to ensure that it doesn't change on each render. This function simply calls setValue with the negation of the current value.

Finally, we return an array containing value and toggleValue so that it can be used in the component that consumes it.

To use this hook in a component, you can import it and call it like this:

import React from "react";
import useToggle from "./useToggle";

function MyComponent() {
  const [isExpanded, toggleExpansion] = useToggle();

  return (
    <div>
      <button onClick={toggleExpansion}>
        {isExpanded ? "Collapse" : "Expand"}
      </button>
      {isExpanded && <p>Some content here</p>}
    </div>
  );
}

In this example, we create a state variable called isExpanded and a toggleExpansion function to update it. We use the useToggle hook to initialize isExpanded to false and provide toggleExpansion as the toggle function.

We then render a button that toggles the isExpanded state variable and changes its text based on the current value. We also conditionally render some content using the isExpanded state variable. When isExpanded is true, the content is shown; when isExpanded is false, the content is hidden.

useIntersectionObserver

Intersection Observer is a browser API that allows you to observe changes to the intersection of a target element with an ancestor element or with the viewport. In React, you can use this API to implement lazy loading, infinite scrolling, and other similar features.

To make React use the Intersection Observer API, you can create a custom hook that uses the useRef and useEffect hooks to create an instance of the Intersection Observer and observe changes to the intersection of the target element.

Here's an example of how to create a useIntersectionObserver hook:

import { useState, useEffect, useRef } from "react";

function useIntersectionObserver(ref, options = {}) {
  const [isIntersecting, setIsIntersecting] = useState(false);
  const observerRef = useRef(null);

  useEffect(() => {
    observerRef.current = new IntersectionObserver(([entry]) => {
      setIsIntersecting(entry.isIntersecting);
    }, options);

    const node = ref.current;
    observerRef.current.observe(node);

    return () => {
      observerRef.current.unobserve(node);
    };
  }, [ref, options]);

  return isIntersecting;
}

In this hook, we define two state variables: isIntersecting to keep track of whether the target element is intersecting with the observer and observerRef to keep a reference to the Intersection Observer instance.

The useEffect hook is used to create and observe changes to the intersection of the target element. We create a new instance of the Intersection Observer and pass a callback function to it that updates the isIntersecting state variable based on the isIntersecting property of the entry object.

We then observe the target element using the observe method of the Intersection Observer instance, and return a cleanup function that unobserves the element using the unobserve method.

Finally, we return the isIntersecting state variable from the hook so that it can be used in the component that consumes it.

To use this hook in a component, you can import it and call it like this:

import React, { useRef } from "react";
import useIntersectionObserver from "./useIntersectionObserver";

function MyComponent() {
  const ref = useRef(null);
  const isIntersecting = useIntersectionObserver(ref);

  return <div ref={ref}>{isIntersecting ? "Visible" : "Not visible"}</div>;
}

In this example, we create a ref using the useRef hook and pass it to the useIntersectionObserver hook. We then render an element and attach the ref to it using the ref prop. Finally, we use the isIntersecting state variable to conditionally render the text "Visible" or "Not visible".

useOnClickOutside

The useOnClickOutside hook is a custom hook that allows you to detect clicks outside of a specified element. In React, you can use this hook to implement features like closing a dropdown menu when the user clicks outside of it or dismissing a modal dialog when the user clicks outside of it.

Here's an example of how to create a useOnClickOutside hook:

import { useEffect, useRef } from "react";

function useOnClickOutside(ref, handler) {
  useEffect(() => {
    const handleClickOutside = (event) => {
      if (ref.current && !ref.current.contains(event.target)) {
        handler();
      }
    };

    document.addEventListener("mousedown", handleClickOutside);

    return () => {
      document.removeEventListener("mousedown", handleClickOutside);
    };
  }, [ref, handler]);
}

In this hook, we use the useEffect hook to add and remove an event listener for mouse clicks outside of the specified element. We also use the useRef hook to create a reference to the element we want to detect clicks outside of.

Inside the useEffect hook, we define a handleClickOutside function that checks if the clicked element is outside of the specified element by using the contains method of the ref.current element. If the clicked element is outside of the specified element, we call the handler function.

We then add the handleClickOutside function as an event listener for the 'mousedown' event using the document.addEventListener method.

Finally, we return a cleanup function that removes the handleClickOutside function from the event listener using the document.removeEventListener method.

To use this hook in a component, you can import it and call it like this:

import React, { useRef } from "react";
import useOnClickOutside from "./useOnClickOutside";

function MyComponent({ onClose }) {
  const ref = useRef();

  useOnClickOutside(ref, onClose);

  return (
    <div ref={ref}>
      <p>Some content here</p>
    </div>
  );
}

In this example, we create a reference to the element we want to detect clicks outside of using the useRef hook. We also call the useOnClickOutside hook and pass the ref and onClose function as arguments. This means that when the user clicks outside of the element, the onClose function will be called.

We then render some content inside the element and set the ref using the ref prop. This allows the useOnClickOutside hook to detect clicks outside of the element and call the onClose function.

useInterval

The useInterval hook is a custom hook that allows you to create a repeating interval in React. In other words, it allows you to run a function at a specified time interval. You can use this hook to implement features like updating the time displayed in a clock or periodically fetching data from an API.

Here's an example of how to create a useInterval hook:

import { useEffect, useRef } from "react";

function useInterval(callback, delay) {
  const savedCallback = useRef();

  useEffect(() => {
    savedCallback.current = callback;
  }, [callback]);

  useEffect(() => {
    function tick() {
      savedCallback.current();
    }
    if (delay !== null) {
      const intervalId = setInterval(tick, delay);
      return () => clearInterval(intervalId);
    }
  }, [delay]);
}

In this hook, we use the useRef hook to create a reference to the callback function. We also use the useEffect hook to update the reference when the callback function changes.

We then use another useEffect hook to create and return an interval that calls the callback function every delay milliseconds. If delay is null, the interval is cleared and no function is called.

To use this hook in a component, you can import it and call it like this:

import React, { useState } from "react";
import useInterval from "./useInterval";

function MyComponent() {
  const [count, setCount] = useState(0);

  useInterval(() => {
    setCount(count + 1);
  }, 1000);

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

In this example, we create a state variable called count and a setCount function to update it. We use the useState hook to initialize count to 0.

We then call the useInterval hook and pass a function that increments count by 1 every second. This means that the count value will be updated every second.

Finally, we render the count value inside a paragraph element using the JSX syntax count. This means that the count value will be displayed on the screen and updated every second.