DEV Community

Cover image for Project 63 of 100 - Reusable Toggler with Custom React Hooks
James Hubert
James Hubert

Posted on

Project 63 of 100 - Reusable Toggler with Custom React Hooks

Hey! I'm on a mission to make 100 React.js projects ending May 31st. Please follow my dev.to profile or my twitter for updates and feel free to reach out if you have questions. Thanks for your support!

Link to today's deployed app: Link
Link to the repo: github

Today is the 3rd day of an impromptu series on React hooks. Today I learned how building a custom hook can replace higher order components in React applications. This was actually a huge relief to me. Way back in project #18 Higher Order Tinder I was building things with Higher Order components, and if you're a Javascript pro they're pretty intuitive but they are large and clunky, and sometimes it's unclear what is happening in them, in my opinion.

As part of React's general overall move to embrace functional components, we have thrown out higher order components and replaced them with hooks.

Take a look at this higher-order component from project #18:

import React from 'react';

export default function TinderCard(component) {
  return function (props) {
    const C = component;
    return (
      <div className='card user-card-container'>
        <div className='card-body user-card-inner'>
          <C />
        </div>
      </div>
    );
  };
}
Enter fullscreen mode Exit fullscreen mode

The above function is pretty understandable, but could be better. At first glance if you didn't know what HOCs are you could probably glean that there is a function within another function and you are passing a component through props to the inner component, and wrapping the passed component . This is weird though, and doesn't follow a pattern like you use really anywhere else in React.

If we were to refactor this into a custom hook, where both functions and variables can be passed directly to another function, we can skip passing things through props entirely. It's not totally clear in this simple example, because we are just passing JSX, but higher order components used to be a de-facto React way of passing things to a component.

Now take a look at the custom hook we create to share a toggler function and a variable in today's project:


import {useState} from 'react'

function useToggler() {
  const [isDefault,setIsOn] = useState(true)

  function toggle() {
    setIsOn(prevState => prevState === true ? false : true)
  }

  return [isDefault,toggle]
}

export default useToggler
Enter fullscreen mode Exit fullscreen mode

First, we don't have to import the entire React library because we don't have to write any JSX in the function, we just import useState in order to keep track of a boolean variable. It's a convention to name a hook starting with the word use- so here we name our hook useToggler. Within the toggler we create a function that can be shared anywhere we want to use this hook.

One of the major benefits of hooks is that they're so reusable. Say you have a bunch of different timekeeping services on your website, for example, then a custom hook could share functions among all of those services. You can return data from the hook in an object, but then you're committing to not being able to rename the properties. If you return the data as an array as I've done here, you can later import the same data and call it whatever you want.

Here is a snippet of relevant code from the App.js component, where we import the useToggler custom hook and use it twice, for two separate toggling items (remember that reusability I mentioned?). I'll show you where we import the hook and set it up for use in both places:

import React from 'react'
import Navbar from './Navbar'
import useToggler from './useToggler'

function App() {
  const [darkIsOff,toggleDarkIsOff] = useToggler();
  const [isJames,toggleIsJames] = useToggler();
  ...
Enter fullscreen mode Exit fullscreen mode

You can see here that we call the useToggler hook twice- once for each piece of the application where we want to use it. Since we returned the data from the hook in an array, we can rename the returned items for what makes sense in each place of the application. Here is the first half of the App component where you can see we are using darkIsOff to determine if dark mode is on or not and using the generic toggler function to toggle the state true or false with the button:

...
<div className={`app ${darkIsOff ? "" : "dark"}`}>
      {/* navbar with title */}
      <Navbar />
      <main className='app__main'>
        {/* dark / light mode toggler */}
        <div className={`app__toggleContainer`}>
          <div className='app__toggleContent'>
            <h2 className='app__togglePrompt'>Toggle Dark Mode</h2>
            <button 
              className='app__toggleButton'
              onClick={toggleDarkIsOff}
            >
              Turn {darkIsOff ? "On" : "Off"}
            </button>
          </div>
        </div>
...
Enter fullscreen mode Exit fullscreen mode

And below is the second part of the App component which uses the same boolean variable and the same toggler function but coming from the custom hook that we called separately and renamed for this piece of the code. The state variable is named isJames here and the function toggleIsJames:

{/* name toggler */}
        <div className={`app__toggleContainer`}>
          <div className='app__toggleContent'>
            <h2 className='app__togglePrompt'>{`My name is${isJames ? "" : " not"} James Hubert`}</h2><br />
            <button 
              className='app__toggleButton'
              onClick={toggleIsJames}
            >
              {isJames ? "This is actually not true." : "Wait, no I am James."}
            </button>
          </div>
        </div>
      </main>
    </div>
Enter fullscreen mode Exit fullscreen mode

So even though it's a simple example you can see immediately that by writing custom hooks, which are just an emergent pattern of React, you can create widely reusable functions, variables, and even JSX for use anywhere in your application. Most importantly, it means you never have to create a higher order component again (if you don't want to) which I find comforting :)

If you like projects like this and want to stay up to date with more, check out my Twitter @jwhubert91, I follow back! See you tomorrow for another project, this time on custom hooks.

Oldest comments (0)