DEV Community

mogwai
mogwai

Posted on

Quick Introduction to React Custom Hooks With Dropdown Selection

Who’s this article for?

For people new to React, and for people who have some experience with React who - like me - become confused when they have to build their own (or read others’) custom hooks. I’ll explain this as simply as I wish it was explained to me.

Skip this if you already understand the basic philosophy of React Hooks. Everyone else, start here:

As if oft drummed into our heads, React is an unopinionated Library that we use to choreograph the front-end. A very important rookie question you have to ask early enough is "why do I need this complicated thing called React when I can just build my front-end using HTML and CSS?”

I think this question is important to ask because it allows you understand the value of React, which lies in something called state.

The simplest definition of state is that it represents the ‘status’ of data passing through a component. React updates the DOM when state changes, and this is something HTML and CSS alone are not equipped for.

Imagine you had a chat app, and you wanted to indicate, with a small ‘status’ icon (usually a tiny cicle) when they are online (green!) or offline (gray!). How would you do that?

In React, you can do this using something called Hooks. Hooks use ‘events’ to update state. If you spoke to the React hook known as useState, this is how the conversation would go:

useState: 'Hello, I'm a React Hook called useState! My job is to keep track of state changes in your React app and update everything according to your programming!"

You: "Oh, hey, useState! So I built a component that keeps track of a user's online activity and changes the color of this tiny circle."

useState: "I see. First, you have to set a default state. This is the state I'll display the circle as when nothing's happening."

You: "How about gray? For offline?"

useState: "Excellent. Works for me! Now you have to set a state setter!"

You: "state setter? What?"

useState: "Oh, it's how you teach me what to look out for to change the default state."

You: "Ah, so I tell the state-setter to watch for the user's 'last seen' status, and when it changes to 'last seen: now', to change the circle to green?"

useState: "Now you're getting it!"

That’s a crash course into hooks.

And now: Custom Hooks

The true beauty of a custom hook is that you can use it to create components that follow state-setting rules all across your application, which makes it easy to make app-wide updates without breaking anything.

Note: The following is adapted from a Frontend Masters class I took.

Imagine we wanted to create a custom hook that gives you a dropdown everytime you invoke it. Why would this be beneficial?

  1. It means that with one custom hook, you can create all kinds of dropdowns.
  2. You can have dropdowns with state tracking baked into them.
  3. You can reuse your dropdowns all over your application.

In code terms, instead of writing this every time you want to make a dropdown,

import React, { useState } from 'react';

const dropdownlist = ["item1", "item2", "item3"]

const FirstDropDown = () => {
  const [firstdropdown, setFirstdropdown] = useState("I am the first!");
return(
  <label htmlFor="First Dropdown">
    First Dropdown
      <select
        id="first"
        value={firstdropdown}
        onChange={e=> setFirstdropdown(e.target.value)}
        onBlur={e=> setFirstdropdown(e.target.value)}
        disabled={!dropdownlist.length}>
          <option>All</option>
          {list.map=>(item=> <option key={item} value={item}>
          {item}
          </item>)}
      </select>
  </label>
);
};

export default FirstDropDown

you write a hook that allows you to create a state-managed custom component instead:

const Dropdownlist = () => {
  return (
  <label>
  <FirstDropdown />
  <SecondDropdown />
  <ThirdDropdown />
</label>
)
};

Let’s begin!

Constructing the Hook

  1. First, create a new jsx document for the custom hook. Keep it in the src folder (if you’re following the conventional setup for React projects):
    file > new > useDropdown.jsx

  2. Inside your new file, import React and useState:

    import React, { useState } from 'react';

Note: something that used to trip me up about React hooks is that you can only use hooks inside a function. I imagine this is because it keeps the hook in local scope and prevents unwanted side effects in global.

  1. Create the dropdown component that you’re going to manage with hooks:

    const DropDown = (label, defaultstate, options) => {
    }

The three arguments above do specific things within the dropdown component, and I’ll mention them now, but they‘ll make sense as we provide.

“Label”: Think of this as the name of the Dropdown itself. So a “Shoes” Dropdown shows a list of shoes to select, and "Shoes” is the label. In HTML, it would be represented like this:

<label htmlFor="Shoes">
    Shoes
</label>

“defaultState” represents, well, the default state of the hook.
“Options” in the case of a dropdown is usually an iterable (ideally, a list) that is used to build the options the user can select from.

Makes sense? Let’s move on!

  1. Define the useState hook (the state setter!)

    const DropDown = (label, defaultstate, options) => {
    const [state, setState) = useState(defaultState);
    }
    
  2. Now, we create the Dropdown component itself

    const useDropdown = (label, defaultstate, options) => {
    const [state, setState) = useState(defaultState);
    const Dropdownmaker = () => (
      <label htmlFor={label}>
        {label}
          <select>
            <option>All</option>
          </select>
    </label>
    )
    }

Basically like you would a regular component.

Now it’s time to plug our state tracker.

  1. Plug the state tracker to auto-populate the Dropdownmaker:
    const useDropdown = (label, defaultstate, options) => {
    const [state, setState) = useState(defaultState);
    const Dropdownmaker = () => (
      <label htmlFor={label}>
        {label}
          <select
          id={label}
          value={state}
          onChange={e=>setState(e.target.value)}
          onBlur={e=>setState(e.target.value)}
          disabled={!options.length}
            >
            <option>All</option>
            {options.map(item=>
            <option key={item} value={item}>{item}</option>)}
          </select>
    </label>
    )
    }

Now we return the necessary values for making the custom hook reusable as an array:

    const useDropdown = (label, defaultstate, options) => {
    const [state, setState) = useState(defaultState);
    const Dropdownmaker = () => (
      <label htmlFor={label}>
        {label}
          <select
          id={label}
          value={state}
          onChange={e=>setState(e.target.value)}
          onBlur={e=>setState(e.target.value)}
          disabled={!options.length}
            >
            <option>All</option>
            {options.map(item=>
            <option key={item} value={item}>{item}</option>)}
          </select>
    </label>
    );
    return [state, Dropdownmaker, setState]
    }

    export default useDropdown

With that, we can now import the custom hook into components that need it!

import React from 'react';
import useDropdown from './useDropdown.jsx';

const shoe_list = ["Prada", "Jimmy Choos", "Nike", "Adidas"]
const Component = () => {
  const [shoe, ShoeDropdown ] = useDropdown("Shoes", "", shoe_list);

  return (
    <ShoeDropdown />    
)
}

Explanation
This custom hook gives you a shoe with a label of “shoes”, a default state of empty array, and an options list of “shoe_list” (which I made into an array above - although ideally you’ll be pulling from an API).

The ShoeDropdown gives you a dropdown list as we designed before, and allows you set the state, which changes the default state based on selection.

And that’s it! A quick introduction to Custom Hooks using Dropdown Selections!

Top comments (6)

Collapse
 
mejanhaque profile image
Muhammad Mejanul Haque • Edited

I want return a specific default value from an array except other values in select option. is that possible? i have an array of categories. in that api i created a default category called Uncategoriezed to set all notcategorized posts. i tried with category[0] method. its not working. if there is a way please tell me...

Collapse
 
vunderkind profile image
mogwai

Hey Mejanhaque!

When you fetch category[0], what do you get as a result? Let me know and I may be able to debug.

[As an aside: the standard pattern for setting categories would be not to add 'uncategorized' to the categories array, but to create a category hook you can reuse for different post components, with a ternary that returns 'Uncategorized' whenever the categories.length test is < 1.

Collapse
 
mejanhaque profile image
Muhammad Mejanul Haque

Thanks for mentioning the standard pattern. should the category value need to be null for fetching the uncategorized posts? and then should i try the categories.length method? I did set the category schema required true in mongoose & express.

when i fetch the category[0].title, it says title is undefined. I am using react hooks. other function worked perfectly. Thanks again for replying.

Thread Thread
 
vunderkind profile image
mogwai

Ah, seen!

Let's try debugging: Instead of fetching category[0].title, see if you can fetch category and either plant a debugger or log category to the console. We need to make sure the fetch is happening correctly, then check to see if the title key is present in the returned array object.

I suspect the problem is from the fetch in your hook, instead of in the assignment syntax afterward. Let me know what is returned from your API when you debug.

Collapse
 
nerdydeedsllc profile image
Nerdy Deeds, LLC

Your initial analogy is apt. More apt than you may think, in fact.

Before you ask yourself about "why can't I do this with html/css," you should probably ask yourself a more important question: "are any of the components I'm intending on adding to this site ACTUALLY REACTIVE?" Because if not? You. Are. Wasting. Your. Time. And. Energy.

The VAST majority of SPA platforms I've used React for should have been/used neither. If you're building a platform that could be produced using simple REST CRUD operations, you should be doing it that way. But it's worse than that, even:

React, Angular, Meteor, the lot of em, were developed 10-13 years ago to fill gaps in JS's capabilities. Newsflash guys: it's been over a decade, JS itself has gone through multiple iterations, and those GAPS NO LONGER EXIST.

At this point, the thing React is BEST at is "being popular."

Collapse
 
oathkeeper profile image
Divyesh Parmar

I used this in my production app, thank you for this