7 Must Know Custom React Hooks in React 18
Tags: react, javascript, frontend, architecture
Published: 2023-03-24 19:10:04

## Introduction:

React 18 has introduced several new features, including concurrent rendering, automatic batching, and more. These improvements have made it easier than ever to create advanced custom hooks, enhancing the overall developer experience. In this article, we will dive into some commonly used custom hooks in React, examine how they have evolved with React 18, and provide code examples to demonstrate their usage.

## 1. useAsync

The useAsync hook makes it simple to handle asynchronous operations like fetching data, API calls, and more. With the new startTransition feature in React 18, we can now create a smoother user experience by delaying visual updates.

```javascript
// Import the useState and useEffect hooks from the React library
import { useState, useEffect } from "react";

// Define a custom hook named useAsync that takes two arguments:
// an async function to execute and an array of dependencies to watch for changes
function useAsync(asyncFunction, deps) {

  // Create three state variables using the useState hook:
  // data, which will hold the result of the async function
  // error, which will hold any error thrown by the async function
  // isLoading, which will track whether the async function is currently executing
  const [data, setData] = useState(null);
  const [error, setError] = useState(null);
  const [isLoading, setIsLoading] = useState(false);

  // Use the useEffect hook to execute the async function whenever any of the dependencies change
  useEffect(() => {
    // Set the isLoading state variable to true
    setIsLoading(true);
    // Execute the async function and update the data and error state variables
    // based on its success or failure, respectively
    asyncFunction()
      .then(setData)
      .catch(setError)
      .finally(() => setIsLoading(false));
  }, deps);

  // Return an object containing the data, error, and isLoading state variables,
  // which can be used by components that use this custom hook to access the results
  // of the async function and track its progress
  return { data, error, isLoading };
}
```

## 2. useDebounce

The useDebounce custom hook prevents unnecessary re-renders by delaying the execution of a function until a specified period of time has passed. React 18's automatic batching feature helps to further optimize performance in these scenarios.

```javascript
// Import the useState and useEffect hooks from the React library
import { useState, useEffect } from "react";

// Define a custom hook named useDebounce that takes two arguments:
// a value to debounce and a delay time in milliseconds
function useDebounce(value, delay) {

  // Create a state variable named debouncedValue using the useState hook
  // and initialize it with the provided value
  const [debouncedValue, setDebouncedValue] = useState(value);

  // Use the useEffect hook to create and clean up a timer that sets the debouncedValue
  // state variable to the provided value after the specified delay time
  useEffect(() => {
    // Define a handler function that sets the debouncedValue state variable
    // to the provided value after the specified delay time
    const handler = setTimeout(() => {
      setDebouncedValue(value);
    }, delay);

    // Return a cleanup function that clears the timer using the clearTimeout function
    return () => clearTimeout(handler);
  }, [value, delay]);

  // Return the debouncedValue state variable, which will be updated after the specified
  // delay time and can be used by components that use this custom hook
  return debouncedValue;
}
```

## 3. usePrevious

The usePrevious custom hook allows you to access the previous value of a prop or state, which can be useful in many scenarios. React 18's new features do not directly impact this hook, but it remains a valuable tool for developers.

```javascript
// Import the useRef and useEffect hooks from the React library
import { useRef, useEffect } from "react";

// Define a custom hook named usePrevious that takes a single argument: a value
function usePrevious(value) {

  // Create a reference variable named ref using the useRef hook
  const ref = useRef();

  // Use the useEffect hook to update the ref variable with the current value
  // whenever the value changes
  useEffect(() => {
    ref.current = value;
  }, [value]);

  // Return the previous value, which is stored in the ref variable
  return ref.current;
}
```

## 4. useDarkMode

The useDarkMode custom hook can be used to toggle between light and dark themes based on user preferences or system settings. React 18's concurrent rendering feature can improve the user experience during theme transitions.

```javascript
// Import the useState and useEffect hooks from the React library
import { useState, useEffect } from "react";

// Define a custom hook named useDarkMode
function useDarkMode() {

  // Create a state variable named isDarkMode using the useState hook
  // and initialize it with a value of false
  const [isDarkMode, setIsDarkMode] = useState(false);

  // Use the useEffect hook to check if the user prefers a dark color scheme
  // and update the isDarkMode state variable accordingly
  useEffect(() => {
    // Check if the user's preferred color scheme matches "dark"
    const prefersDarkMode = window.matchMedia(
      "(prefers-color-scheme: dark)"
    ).matches;
    // Update the isDarkMode state variable with the result
    setIsDarkMode(prefersDarkMode);
  }, []);

  // Return an array containing the isDarkMode state variable and the setIsDarkMode function,
  // which can be used by components that use this custom hook to read from and write to
  // the corresponding value for the user's preferred color scheme
  return [isDarkMode, setIsDarkMode];
}
```

## 5. useLocalStorage

The useLocalStorage custom hook allows you to synchronize a state with the browser's localStorage, making it easy to persist data across page refreshes or multiple sessions. This hook works seamlessly with React 18's features.

```javascript
// Import the useState and useEffect hooks from the React library
import { useState, useEffect } from "react";

// Define a custom hook named useLocalStorage that takes two arguments:
// a key to identify the stored value and an initial value to use if the key
// is not found in localStorage
function useLocalStorage(key, initialValue) {

  // Create a state variable named storedValue using the useState hook
  // and initialize it with the value retrieved from localStorage, if present,
  // or the provided initial value otherwise
  const [storedValue, setStoredValue] = useState(() => {
    try {
      const item = window.localStorage.getItem(key);
      return item ? JSON.parse(item) : initialValue;
    } catch (error) {
      console.error("Error reading from localStorage:", error);
      return initialValue;
    }
  });

  // Define a setValue function that updates the storedValue state variable
  // and stores the new value in localStorage using the provided key
  const setValue = (value) => {
    try {
      // If the provided value is a function, use it to calculate the new value
      const valueToStore = value instanceof Function ? value(storedValue) : value;
      // Update the storedValue state variable
      setStoredValue(valueToStore);
      // Store the new value in localStorage using the provided key
      window.localStorage.setItem(key, JSON.stringify(valueToStore));
    } catch (error) {
      console.error("Error writing to localStorage:", error);
    }
  };

  // Return an array containing the storedValue state variable and the setValue function,
  // which can be used by components that use this custom hook to read from and write to
  // the corresponding value in localStorage
  return [storedValue, setValue];
}

```

## 6. useInterval

The useInterval custom hook provides a convenient way to manage intervals within your React components. It allows you to set up and clean up timers automatically based on component lifecycle events. React 18's new features do not directly impact this hook, but it remains a useful utility for managing periodic tasks.

```javascript
// Import the useRef and useEffect hooks from the React library
import { useRef, useEffect } from "react";

// Define a custom hook named useInterval which takes two arguments:
// a callback function and a delay time in milliseconds
function useInterval(callback, delay) {

  // Create a reference to the callback function using the useRef hook
  const savedCallback = useRef();

  // Use the useEffect hook to update the savedCallback reference whenever
  // the callback function changes
  useEffect(() => {
    savedCallback.current = callback;
  }, [callback]);

  // Use the useEffect hook to create and clean up an interval timer
  // based on the provided delay time
  useEffect(() => {

    // Define a tick function that calls the savedCallback function
    const tick = () => {
      savedCallback.current();
    };

    // If the delay time is not null, create a new interval timer
    // using the setInterval function and return a cleanup function
    // that clears the interval timer using the clearInterval function
    if (delay !== null) {
      const id = setInterval(tick, delay);
      return () => clearInterval(id);
    }
  }, [delay]);
}
```

## 7. useWindowSize

The useWindowSize custom hook allows you to access and respond to the current window size. It can be helpful for creating responsive designs or handling resizing events. React 18's new features do not directly impact this hook, but it remains a valuable tool for developers.

You can use this hook in your components to get the current window size and respond to changes in size. For example, you can conditionally render certain components based on the window width or height.

```javascript
// Import the useState and useEffect hooks from the React library
import { useState, useEffect } from "react";

// Define a custom hook named useWindowSize
function useWindowSize() {

  // Create a state variable named windowSize using the useState hook
  // and initialize it with an object containing the current window's width and height
  const [windowSize, setWindowSize] = useState({
    width: window.innerWidth,
    height: window.innerHeight
  });

  // Define a handleResize function that updates the windowSize state variable
  // with the current window's width and height whenever the window is resized
  const handleResize = () => {
    setWindowSize({
      width: window.innerWidth,
      height: window.innerHeight
    });
  };

  // Use the useEffect hook to add and remove a "resize" event listener
  // that calls the handleResize function whenever the window is resized
  useEffect(() => {
    window.addEventListener("resize", handleResize);
    return () => {
      window.removeEventListener("resize", handleResize);
    };
  }, []);

  // Return the windowSize state variable, which will be updated whenever the
  // window is resized and can be used by components that use this custom hook
  return windowSize;
}
```

## Connect with me!
[LinkedIn](https://www.linkedin.com/in/lucas-jordan-stifano/)


## Conclusion:

React 18 has ushered in a new era of performance and user experience improvements. By leveraging the latest features and building advanced custom hooks, developers can create more efficient and powerful applications. The examples provided in this article demonstrate how these hooks can be adapted and improved with React 18, offering new possibilities for React developers.