Skip to main content

Creating Your First Custom Hook: `useToggle` (Part 2) - Adding More Features #83

📖 Introduction

Following our creation of a simple useToggle hook, this article explores how we can make it even more powerful. We will add features to our useToggle hook that allow us to set the value to true or false directly, in addition to toggling it.


📚 Prerequisites

Before we begin, please ensure you have a solid grasp of the following concepts:

  • All concepts from Part 1 of this series.
  • The useReducer hook.

🎯 Article Outline: What You'll Master

In this article, you will learn:

  • The useReducer Hook: Why useReducer is a good choice for managing state with multiple actions.
  • Core Implementation: How to refactor the useToggle hook to use useReducer.
  • Practical Application: How to use the enhanced useToggle hook in a component.

🧠 Section 1: The Core Concepts of useReducer for useToggle

Our current useToggle hook is great, but it only allows us to toggle the value. What if we want to explicitly set the value to true or false? We could add more functions to our hook, but a cleaner approach is to use the useReducer hook.

useReducer is an alternative to useState. It is usually preferable to useState when you have complex state logic that involves multiple sub-values or when the next state depends on the previous one.

For our useToggle hook, we can use a reducer to handle three actions: TOGGLE, SET_TRUE, and SET_FALSE.


💻 Section 2: Deep Dive - Implementation and Walkthrough

Let's refactor our useToggle hook to use useReducer.

2.1 - The useToggle Hook with useReducer

// useToggle.js
import { useReducer, useCallback } from 'react';

const toggleReducer = (state, action) => {
switch (action.type) {
case 'TOGGLE':
return !state;
case 'SET_TRUE':
return true;
case 'SET_FALSE':
return false;
default:
return state;
}
};

function useToggle(initialValue = false) {
const [value, dispatch] = useReducer(toggleReducer, initialValue);

const toggle = useCallback(() => dispatch({ type: 'TOGGLE' }), []);
const setTrue = useCallback(() => dispatch({ type: 'SET_TRUE' }), []);
const setFalse = useCallback(() => dispatch({ type: 'SET_FALSE' }), []);

return [value, toggle, setTrue, setFalse];
}

export default useToggle;

Step-by-Step Code Breakdown:

  1. toggleReducer: We define a reducer function that takes the current state and an action, and returns the new state.
  2. useReducer(toggleReducer, initialValue): We use the useReducer hook with our reducer and an initial value. It returns the current state (value) and a dispatch function.
  3. dispatch({ type: '...' }): We create memoized functions (toggle, setTrue, setFalse) that call the dispatch function with the appropriate action type.
  4. return [value, toggle, setTrue, setFalse];: We return the current value and our new functions.

🛠️ Section 3: Project-Based Example: A Modal Component

Now, let's use our enhanced useToggle hook to manage a modal component.

// ModalComponent.js
import React from 'react';
import useToggle from './useToggle';

function ModalComponent() {
const [isOpen, toggle, openModal, closeModal] = useToggle(false);

return (
<div>
<button onClick={openModal}>Open Modal</button>
{isOpen && (
<div className="modal">
<div className="modal-content">
<p>This is a modal!</p>
<button onClick={closeModal}>Close</button>
</div>
</div>
)}
</div>
);
}

export default ModalComponent;

Code Breakdown:

  1. const [isOpen, toggle, openModal, closeModal] = useToggle(false);: We destructure the values from our useToggle hook. We can rename setTrue to openModal and setFalse to closeModal for better readability.
  2. <button onClick={openModal}>: We use the openModal function to open the modal.
  3. <button onClick={closeModal}>: We use the closeModal function to close the modal.

💡 Conclusion & Key Takeaways

In this article, we've made our useToggle hook even more powerful by using the useReducer hook. We can now not only toggle the value, but also set it to true or false directly.

Let's summarize the key takeaways:

  • useReducer is a great choice for managing state with multiple actions.
  • By using useReducer, we can make our custom hooks more flexible and powerful.
  • We can rename the functions returned from our custom hooks for better readability in our components.

Challenge Yourself: To solidify your understanding, try to add a reset action to the useToggle hook that sets the value back to its initial state.


➡️ Next Steps

You now have a solid understanding of how to create a more advanced custom hook. In the next article, "A Custom Hook for Data Fetching: useFetch (Part 1)", we will build a reusable hook for fetching data from an API.

Thank you for your dedication. Stay curious, and happy coding!


glossary

  • useReducer: A React hook that is an alternative to useState for managing state with complex logic.
  • Reducer: A pure function that takes the current state and an action, and returns the new state.
  • Dispatch: A function that sends an action to a reducer.

Further Reading