DEV Community

loading...
Cover image for Most Awesome Hooks for your React project

Most Awesome Hooks for your React project

brayanarrieta profile image Brayan Arrieta ・3 min read

React.js is currently one of the most popular JavaScript libraries for front-end developers.

React really changed the way we build Single-page applications (SPAs). One of its greatest features is hooks that introduced in React 16.8 and that new feature enables the possibility of using functional components instead of class components handling the state with the Hooks.

React offers ways to implement our own custom hooks. Some awesome custom hooks are:

useTimeout Hook

With this custom hook, we can implement a javascript setTimeout using a declarative approach.

Code

import { useEffect, useRef } from 'react';

const useTimeout = (callback, timeout) => {
  const savedCallback = useRef(null);
  savedCallback.current = callback;

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

  useEffect(
    () => {
      if (timeout) {
        const timeoutId = setTimeout(() => {
          savedCallback.current();
        }, timeout);
        return () => clearTimeout(timeoutId)
      }
    },
    [timeout]
  )
}
Enter fullscreen mode Exit fullscreen mode

Example of use

import { useState } from 'react';

const ExampleComponent = () => {
  const [message, setMessage] = useState('');
  useTimeout(() => {
    setMessage('Hello World');
  }, 7500);

  return (<p>{message}</p>);
}
Enter fullscreen mode Exit fullscreen mode

Edit useTimeout

usePrevious Hook

With this custom hook, we can have access to the previous state related to the components.

Code

import { useEffect, useRef } from 'react';

const usePrevious = (state) =>  {
  const ref = useRef();

  useEffect(() => {
    ref.current = state;
  });

  return ref.current;
}
Enter fullscreen mode Exit fullscreen mode

Example of use

import { useState } from 'react';

const ExampleComponent = () => {
  const [counter, setCounter] = useState(0);
  const previousCounter = usePrevious(counter);

  return (
    <>
      <p>Counter: {counter}</p>
      <p>Previous Counter: {previousCounter}</p>
      <button onClick={() => setCounter(counter + 1)}>Next</button>
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

Edit usePrevious

useInterval Hook

With this custom hook, we can implement javascript setInterval using a declarative approach.

Code

import { useEffect, useRef } from 'react';

const useInterval = (callback, delay) => {
  const savedCallback = useRef(null);
  savedCallback.current = callback;

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

  useEffect(
    () => {
      if (delay) {
        const intervalId = setInterval(() => {
          savedCallback.current();
        }, delay);
        return () => clearInterval(intervalId)
      }
    },
    [delay]
  )
}
Enter fullscreen mode Exit fullscreen mode

Example of use

import { useState } from 'react';

const ExampleComponent = () => {
  const [seconds, setSeconds] = useState(0);
  useInterval(() => {
    setSeconds(seconds + 1);
  }, 1000);

  return <p> Seconds passed: {seconds}</p>;
}
Enter fullscreen mode Exit fullscreen mode

Edit useInterval

useFetch Hook

The useFetch hook can be used to implement fetch in a declarative way. Also, this custom hook helps with behaviors as loading and errors.

Code

import { useState, useEffect } from 'react';

const useFetch = (initialUrl, initialOptions = {}) => {
  const [url, setUrl] = useState(initialUrl);
  const [options, setOptions] = useState(initialOptions);
  const [data, setData] = useState(null);
  const [error, setError] = useState(null);
  const [isLoading, setIsLoading] = useState(true);

  useEffect(() => {
    const fetchData = async() => {
      try {
        setIsLoading(true);
        const response = await fetch(url, options);
        const json = await response.json();
        setData(json);
      } catch (err) {
        setError(err);
      } finally {
        setIsLoading(false);
      }
    }
    fetchData();
  }, [url, options]);

  return ({data, error, isLoading, setUrl, setOptions});
};
Enter fullscreen mode Exit fullscreen mode

Example of use

const URL = 'https://jsonplaceholder.typicode.com/todos';

const ExampleComponent = () {
  const { data, error, isLoading } = useFetch(URL);

  if(isLoading) {
    return (<p>Loading...</p>)
  }

  if (error) {
    return <p>{error?.message}</p>;
  }

  const renderItem = ({id, title})=> (
    <div key = {`item-${id}`}>
      <p>{id} - {title}</p>
    </div>
  );

  return data.map(renderItem);
}
Enter fullscreen mode Exit fullscreen mode

Edit useFetch

useContructor hook

The useContructor hook can be used to implement the same behavior as class components.

Code

import React from 'react';

export const useConstructor = (callBack = () => {}) => {
  const [hasBeenCalled, setHasBeenCalled] = React.useState(false);
  if (hasBeenCalled) return;
  callBack();
  setHasBeenCalled(true);
};


Enter fullscreen mode Exit fullscreen mode

I will be updating the post with new awesome Hooks

Discussion (5)

Collapse
rxliuli profile image
rxliuli

React hooks has changed the way of declaring state from the language level to the framework level, so many existing front-end codes (timeout/interval/debounce) need to be repackaged and encapsulated in hooks for this reason.

P.S. Some are indeed strongly related to react and ui. It is indeed necessary to encapsulate, but rewriting just to be compatible with hooks makes me feel bad.

Collapse
yazdanimahdi profile image
Mahdi Yazdani

Great resources, it would be helpful if you could publish these as a NPM package so we can install and give proper credit in our projects.

Collapse
brayanarrieta profile image
Brayan Arrieta Author • Edited

I started today with the creation of a package to provide those awesome hooks probably in the next days will be published.

Thx for the recommendation @yazdanimahdi

Collapse
ratneshmurugesan profile image
Ratnesh Murugesan • Edited

In useFetch hook, inside useEffect, either url or options will trigger Infinite rerender if they are declared as an object. Please pass them as a primitive type value to avoid the same.

Collapse
brayanarrieta profile image
Brayan Arrieta Author

Good catch I will check that, rigth now I'm working in a library so I will keep that in mind and after that I will update this example

Forem Open with the Forem app