In this article, I will explore six custom hooks for ReactJS that can help simplify your code and improve the performance of your applications. We will provide examples for each hook, as well as tips for creating custom hooks that are reusable, flexible, and easy to understand.

Introduction: What are React Hooks?

Due to its adaptability, scalability, and high performance, ReactJS has grown to be one of the most well-known and frequently used front-end JavaScript frameworks. Using reusable components, it enables developers to construct dynamic and interactive user interfaces. React 16.8 saw the introduction of a brand-new feature called hooks. This dramatically altered how programmers handle side effects and maintain the states in React apps.

React hooks give functional components. Earlier, it was restricted to utilizing just stateless functional components, a mechanism to reuse stateful code. Developers may now construct unique hooks that include reusable stateful logic, making it simple and effective to handle complicated states and side effects.

Commonly used ReactJS custom Hooks

useInput

The useInput hook simplifies input validation and formatting in a ReactJS application. Here’s an example of how it can be used:

import { useState } from 'react';

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

  function handleChange(event) {
    setValue(event.target.value.trim());
  }

  return {
    value,
    onChange: handleChange,
  };
}

function MyForm() {
  const nameInput = useInput('');
  const emailInput = useInput('');

  function handleSubmit(event) {
    event.preventDefault();
    // Do something with nameInput.value and emailInput.value
  }

  return (
    <form onSubmit={handleSubmit}>
      <label htmlFor="name">Name:</label>
      <input id="name" {...nameInput} />

      <label htmlFor="email">Email:</label>
      <input id="email" {...emailInput} />

      <button>Submit</button>
    </form>
  );
}

In this example, the useInput hook creates two input fields for a form. The hook then returns an object with a value property and a handleChange function that updates the value whenever the input field changes. The spread then creates the input fields in order to pass in the nameInput and emailInput objects.

useDebounce

The useDebounce hook delays a function’s execution until a certain time has passed, hence useful for search bars and filtering. Here’s an example of how one can use it:

import { useState, useEffect } from 'react';

function useDebounce(value, delay) {
  const [debouncedValue, setDebouncedValue] = useState(value);

  useEffect(() => {
    const timer = setTimeout(() => {
      setDebouncedValue(value);
    }, delay);

    return () => {
      clearTimeout(timer);
    };
  }, [value, delay]);

  return debouncedValue;
}

function MySearchBar() {
  const [searchTerm, setSearchTerm] = useState('');
  const debouncedSearchTerm = useDebounce(searchTerm, 500);

  function handleSearch(event) {
    setSearchTerm(event.target.value);
  }

  useEffect(() => {
    // Call API with debouncedSearchTerm
  }, [debouncedSearchTerm]);

  return (
    <input type="text" value={searchTerm} onChange={handleSearch} />
  );
}

In this example, I use the useDebounce hook to delay the execution of an API call until the user has stopped typing for 500 milliseconds. The useEffect hook now calls the API with the debouncedSearchTerm variable, which updates whenever the searchTerm variable changes.

useLocalStorage

The useLocalStorage hook lets you easily store and retrieve data from the browser’s local storage. Here’s an example of how you can use it:

import { useState } from 'react';

function useLocalStorage(key, initialValue) {
  const [storedValue, setStoredValue] = useState(() => {
    const item = window.localStorage.getItem(key);
    return item ? JSON.parse(item) : initialValue;
  });

  function setValue(value) {
    setStoredValue(value);
    window.localStorage.setItem(key, JSON.stringify(value));
  }

  return [storedValue, setValue];
}

function MyComponent() {
  const [name, setName] = useLocalStorage('name', '');

  function handleNameChange(event) {
setName(event.target.value);
}

return (
<div>
<label htmlFor="name">Name:</label>
<input id="name" type="text" value={name} onChange={handleNameChange} />
<p>Hello, {name}!</p>
</div>
);
}

In the example above, I use the useLocalStorage hook to store and retrieve the value of a name variable in the browser’s local storage. The useState hook however initializes the name variable with the stored value, or with an empty string if no value is stored. The setName function now updates the value of the name variable and store it in the local storage.

useKeyPress

The useKeyPress hook listens for keyboard input and triggers a function when a specific key is pressed. Here’s an example of how it can be used:

import { useState, useEffect } from 'react';

function useKeyPress(targetKey, callback) {
function handleKeyPress(event) {
if (event.key === targetKey) {
callback();
}
}

useEffect(() => {
window.addEventListener('keydown', handleKeyPress);
return () => {
window.removeEventListener('keydown', handleKeyPress);
};
}, []);
}

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

useKeyPress('ArrowUp', () => setCount(count + 1));
useKeyPress('ArrowDown', () => setCount(count - 1));

return (
<div>
<p>Count: {count}</p>
</div>
);
}

In the following example, the useKeyPress hook increments or decrements a count variable when the user presses the up or down arrow keys, respectively. The useEffect hook then adds and remove an event listener for the keydown event, and the handleKeyPress function checks whether the pressed key is the target key and calls the provided callback function.

useClickOutside

The useClickOutside hook detects clicks outside of a specified element and triggers a function. Here’s an example of how it can be used:

import { useRef, useEffect } from 'react';

function useClickOutside(ref, callback) {
function handleClick(event) {
if (ref.current && !ref.current.contains(event.target)) {
callback();
}
}

useEffect(() => {
document.addEventListener('click', handleClick);
return () => {
document.removeEventListener('click', handleClick);
};
}, []);
}

function MyComponent() {
const modalRef = useRef(null);

function handleCloseModal() {
// Close the modal
}

useClickOutside(modalRef, handleCloseModal);

return (
<div>
<div ref={modalRef}>
<p>Modal content</p>
</div>
</div>
);
}

In this example, the useClickOutside hook closes a modal dialog when the user clicks outside of it. The useRef hook then creates a reference to the modal element, the useClickOutside hook now adds or remove an event listener for the click event, and the handleClick function checks whether the clicked element is outside of the modal and calls the provided callback function.

usePrevious

The usePrevious hook stores the previous value of a state variable, useful for tracking changes and triggering actions. Here’s an example of how it can be used:

import { useState, useEffect } from 'react';

function usePrevious(value) {
const [value, setPrevious] = useState(null);

useEffect(() => {
setPrevious(value);
}, [value]);

return previous;
}

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

useEffect(() => {
if (previousCount !== null && count !== previousCount) {
console.log('Count changed from', previousCount, 'to', count);
}
}, [count, previousCount]);

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

In the example above, the usePrevious hook stores the previous value of the count variable, and the useEffect hook checks for changes in the count variable and logs the previous and current values. The previousCount variable then updates using the usePrevious hook whenever count changes.

How to start

If you’re new to React hooks, it can be overwhelming to learn all of them at once. I recommend you start with the basics, such as useState and useEffect, and gradually work your way up to more advanced hooks like the ones discussed here.

When you want to create your custom hooks, it’s important to follow the rules of hooks and ensure that your hooks are reusable and easy to understand.

Creating custom hooks in React is all about creating reusable and flexible code that can be easily integrated into your applications. When you create a custom hook, you are essentially encapsulating some piece of stateful logic that can be shared across multiple components. This can save you a lot of time and effort when it comes to managing the state and handling side effects in your application.

Tips to get started in creating your own ReactJS custom hooks

Some tips for creating custom hooks include:

  • Start with a clear use case

Before creating a custom hook, make sure you have a clear use case in mind. This will help you design the hook in a way that is useful and easy to use.

  • Name your hook clearly

Use a descriptive name for your hook that accurately reflects its purpose.

  • Keep it simple

Don’t try to do too much in a single hook. Keep your hooks simple and focused on a specific task.

  • Document your hook

Provide clear documentation and examples for your hook so that others can understand how to use it.

  • Test your hook

Test your hook thoroughly to ensure that it works as expected in a variety of scenarios.

By following these guidelines, you can create high-quality custom hooks. This will save you time and make your code more readable and maintainable.

Example 1

Here’s an example of how you can create a custom hook that fetches data from an API:

import { useState, useEffect } from "react";

function useFetch(url) {
  const [data, setData] = useState([]);
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    async function fetchData() {
      try {
        const response = await fetch(url);
        const data = await response.json();
        setData(data);
        setIsLoading(false);
      } catch (error) {
        setIsLoading(false);
        setError(error);
      }
    }
    fetchData();
  }, [url]);

  return { data, isLoading, error };
}

export default useFetch;

In this example, I have defined a custom hook called useFetch that takes a URL parameter and uses the useState and useEffect hooks to fetch data from the specified URL.

The useState hook defines three states: data, isLoading, and error.

Here

  1. data will hold the data returned from the API
  2. isLoading indicates whether the data is being loaded.
  3. the error will hold any errors that occur during the fetch process.

The useEffect hook defines the logic for fetching the data. I defined an asynchronous function called fetchData that uses the fetch API to request the specified URL. I updated the data state with the response data and set isLoading to false after the data was returned. If an error occurs during the fetch process, I set isLoading to false and update the error state with the error object.

Finally, we return an object that contains the data, isLoading, and error states. This object can be used in any component that imports the useFetch hook to easily fetch data from an API. Also, manages the loading and error states.

Example 2

Here’s how you can use this custom hook in a component:

import useFetch from "./useFetch";

function MyComponent() {
  const { data, isLoading, error } = useFetch("https://jsonplaceholder.typicode.com/posts");

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

  if (error) {
    return <div>Error: {error.message}</div>;
  }

  return (
    <div>
      {data.map((item) => (
        <div key={item.id}>
          <h3>{item.title}</h3>
          <p>{item.body}</p>
        </div>
      ))}
    </div>
  );
}

export default MyComponent;

In this example, we import the useFetch hook and use it to fetch data from the JSONPlaceholder API. We then render the data as a list of posts, handling the loading and error states with conditional rendering.

Conclusion

React hooks provide a powerful and flexible way to manage state and handle side effects in React applications. We learned how to use built-in hooks and create custom Reactjs hooks. This allowed us to write clean, reusable, and efficient code that is easy to understand and maintain.

Categorized in: