Custom React Hooks

Gautam JainGautam Jain
6 min read

React is one of the most powerful and popular frameworks being used today, because of its features like declarative syntax, component-based and operator independence.

One of the most awesome features of react is the ability to create custom hooks.

What are Custom hooks?

A custom Hook is a JavaScript function whose name starts with ”use” and that may call other Hooks.

From the above statement, we can understand that react has given us the abillity to create hooks like a function which can perform specific tasks, even using other hooks inside of it, separate from our main codebase.

Why do we need Custom hooks?

It is similar to thinking, why do we need components? The answer is simple

Custom react hooks offer reusability as when a custom hook is created, it can be reused easily, which makes the code cleaner and reduces the time to write the code(as in the case of component layout). It also enhances the rendering speed of the code as a custom hook does not need to be rendered again and again while rendering the whole code.

Some Examples of custom hooks

some of the custom hooks I created while making projects and they make life much simpler by taking care of tedious tasks behind the scenes.

useAsync

A powerful custom which helps in resolving a promise asynchronously.

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

// a custom hook to resolve a promise asyncronusly

export default function useAsync(callback, dependencies = []) {
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);
  const [value, setValue] = useState();

  // whenever our dependencies change, useCallback provides us with a new callbackMemoized
  const callbackMemoized = useCallback(() => {
    setLoading(true);
    setError(undefined);
    setValue(undefined);
    callback()
      .then(setValue)
      .catch(setError)
      .finally(() => setLoading(false));
  }, dependencies);

  // our useEffect triggers the callback memoized whenever it changes
  useEffect(() => {
    callbackMemoized();
  }, [callbackMemoized]);

  return { loading, error, value };
}

useChangeLogger

A simple react hook that logs the value to the console whenever the value changes.

import { useEffect, useRef } from 'react'

// A simple react hook that logs the value to the console whenever the value chnages.

function useChangeLogger(value) {
    const firstRenderRef = useRef(true)
    useEffect(() => {
        if (firstRenderRef.current) {
            firstRenderRef.current = false
            return
        }
        console.log("Value Changed!")
        console.log({ value })
        //  alert("Value Changed! new value: " + value)
  }, [value])
}

export default useChangeLogger

useCopyToClipboard

A custom hook that lets you copy text to the clipboard.

import { useState } from 'react';
import copy from 'copy-to-clipboard';

export default function useCopyToClipboard() {
  const [value, setValue] = useState();
  const [success, setSuccess] = useState();

  const copyToClipboard = (text, options) => {
    const result = copy(text, options);
    if (result) setValue(text);
    setSuccess(result);
  };

  return [copyToClipboard, { value, success }];
}

useDocumentTitle

A simple custom hook that lets to set a document title to a webpage.

import { useEffect } from 'react'

// we are using the useEffect hook in order to set the document title. 
// The document title is an important part of any website, because it:

// is displayed in the browser tab
// is also displayed in the browser history
// helps with accessibility and search engine optimization (SEO)

function useDocumentTitle(pageTitle) {

 useEffect(() => {
    document.title = pageTitle
  }, [pageTitle])
}

export default useDocumentTitle

useEventListener

A custom hook used to add an event listener to a custom element.

import { useEffect, useRef } from 'react'

// hook to add a event listener to a target
const useEventListener = (
  eventType = '',
  listener = () => null,
  target = window,
  options = null
) => {
  const savedListener = useRef();

  useEffect(() => {
    savedListener.current = listener;
  }, [listener]);

  useEffect(() => {
    if (!target?.addEventListener) return;

    // get event listener
    const eventListener = event => savedListener.current(event);

    // add a event listener to the target
    target.addEventListener(eventType, eventListener, options);

    // remove the event listener in the cleanup of useEffect  
    return () => {
      target.removeEventListener(eventType, eventListener, options);
    };
  }, [eventType, target, options]);
};

export default useEventListener;

useForm

A custom hook to handle form events

import { useState } from 'react';

export const useForm = (initialState = {}) => {
  const [values, setValues] = useState(initialState);

  const reset = () => {
    setValues(initialState);
  };

  const handleInputChange = ({ target }) => {
    setValues({
      ...values,
      [target.name]: target.value,
    });
  };

  return [values, handleInputChange, reset];
};

usePrevious

A custom hook is that helps get the previous value of a state variable.

import { useRef } from 'react';

// a custom hook to get the previous value of a state

export default function usePrevious(value) {
  // refs for current and the previous value
  const currentRef = useRef(value);
  const previousRef = useRef(null);

    // check if the current value as changed
  if (currentRef.current !== value) {
    previousRef.current = currentRef.current;
    currentRef.current = value;
  }

  return previousRef.current;
}

useStateWithHistory

Similar to usePrevious hook but better as it stores all the history of a state variable and provides functionality to go to any timeline.

import { useCallback, useRef, useState } from 'react';

const CAPACITY = 10;

// A custom hook which stores prev values of the state (CAPCITY) and provides you
// full control over any of the stored previous state

export default function useStateWithHistory(
  defaultValue,
  { capacity = CAPACITY } = {}
) {
  // state for currentvalue, refs for history array and pointer to the history array
  const [value, setValue] = useState(defaultValue);
  const historyRef = useRef([value]);
  const pointerRef = useRef(0);

  // we modify the set operation
  const set = useCallback(
    (v) => {
      //   calculate the value of the current
      const resolvedValue = typeof v === 'function' ? v(value) : v;

      // if we find that the value has changed
      if (historyRef.current[pointerRef.current] !== resolvedValue) {
        // delete all instances of the state after the pointer
        if (pointerRef.current < historyRef.current.length - 1) {
          historyRef.current.splice(pointerRef.current + 1);
        }
        // push the new resolvedValue to the history array
        historyRef.current.push(resolvedValue);

        // check for the capacity and shift the least recent value
        while (historyRef.current.length > capacity) {
          historyRef.current.shift();
        }
        // set the pointer
        pointerRef.current = historyRef.current.length - 1;
      }
      setValue(resolvedValue);
    },
    [capacity, value]
  );

    // utility function to go back in state history
  const back = useCallback(() => {
    if (pointerRef.current <= 0) return;
    pointerRef.current--;
    setValue(historyRef.current[pointerRef.current]);
  }, []);

    // utility function to go forward in state history
  const forward = useCallback(() => {
    if (pointerRef.current >= historyRef.current.length - 1) return;
    pointerRef.current++;
    setValue(historyRef.current[pointerRef.current]);
  }, []);

    // utility dunction to go a specific point / index in history array
  const go = useCallback((index) => {
    if (index < 0 || index > historyRef.current.length - 1) return;
    pointerRef.current = index;
    setValue(historyRef.current[pointerRef.current]);
  }, []);

  return [
    value,
    set,
    {
      history: historyRef.current,
      pointer: pointerRef.current,
      back,
      forward,
      go,
    },
  ];
}

Conclusion

As you saw above there can be many kinds of custom hooks and some can be as simple as logging a variable to the console. Custom Hooks are very useful in organizing code and keeping bulky, hard-to-read code separate.

Custom React Hooks

This is the link to my GitHub repository with more such custom hooks. Feel free to take a look and enjoy.

If you enjoyed reading Please give a star to the article and the repository

Have an amazing coding day :)

20
Subscribe to my newsletter

Read articles from Gautam Jain directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Gautam Jain
Gautam Jain

Hi Guys, my name is Gautam Jain and I have been fascinated by the wondrous aspects of technology ever since i was a kid, and after getting my personal computer after 12th standard my interest sky-rocketed. I was so amazed by the cool operations one can perform using such small machine like a laptop and a phone. I begin to dive deep into the concepts of the computer science and to get more proficient in the field, enrolled for a Computer Science undergraduate degree. I graduated with my bachelors degree from GNDEC, Ludhiana in august 2023. Throughout my course of 4 years, I invested in developing a good understanding core computer science concepts like discrete mathematics, Data Structures and Algorithms, Operating systems, Database management, CAM, Cyber Security, Compiler Design etc. Along with my academics, i also invested in developing my problem solving skills by actively indulging in competitive tournaments on platforms like GeeksforGeeks, Leetcode, Code studio, Hacker rank, Code chef and also made a routine of solving daily problem and challenges. I worked together with my team (college friends and work colleagues) to create innovative, scalable and open-source projects and gained experience in software development by working as student SDE intern at E-Cell, TNP Cell and 11 mantras. I am currently working as a Programmer Analyst trainee in Cognizant Technology Solutions, brushing my skills in technologies' like Oracle Apps, Oracle SQL, Oracle PL/SQL, Java, JSP, JDBC, Spring etc. I am always open to opportunities to further polish my skills and get a insight to how the daily world functions with the help of technology. Actively Looking for open technology and project idea discussions.