How to use checkbox inside select options? React hook

Viewed 4674

Currently I'm trying to create this design.

hoo

It doesn't open and close as expected. I also created a codesandbox

Three things:
1. I have a onClick, but I'm not sure if my logic is correct to open and close the button.Also should there be a useEffect here to listen to changes?

const showPlatformOptions =()=> {
  //  let checkboxes = el;
  //  console.log("ref:",myRef.current)
  //   if (!expanded) {
  //    //checkboxes.style.display = "block";
  //     setExpanded(true);
  //   } else {
  //   // checkboxes.style.display = "none";
  //     setExpanded(false);
  //   }


  }
  1. I have a onChange called selectionOptions that should let me know which platforms are selected, but it currently only shows one platform at a time, why?

  2. Is there another way to create this dropdown and checkbox. Maybe a library using hooks?

Any help is appreciated.

import React, { useState,useEffect, useRef} from "react";


const SearchBar =()=>{
  const [query, setQuery] = useState("");
  const [expanded,setExpanded] = useState(false);
  const [selectionOptions, setSelectionOptions] = useState(["Instagram","LinkedIn","Twitter"]);
  const myRef = useRef(null);

  const showPlatformOptions =()=> {
  //  let checkboxes = el;
  //  console.log("ref:",myRef.current)
  //   if (!expanded) {
  //    //checkboxes.style.display = "block";
  //     setExpanded(true);
  //   } else {
  //   // checkboxes.style.display = "none";
  //     setExpanded(false);
  //   }


  }

  const handleQueryChange = event => {
    console.log("Event:",event.target.value)
    setQuery(event.target.value);
  };

  return (
    <form onSubmit={showPlatformOptions}>
      <div className="w-64">
        <div className="relative" onClick={showPlatformOptions}>
          <h6>PLATFORMS </h6>
          <select className="w-full font-semibold"  onChange={handleQueryChange}>
              {selectionOptions.map((platform,x) => (
                  <option key={x} value={platform}>
                      {platform}
                  </option>
              ))}
          </select>
          <div className="absolute left-0 right-0 top-0 bottom-0"></div>
        </div>
        <div 
        ref={myRef}
        className="checkboxes border-gray-200 border border-solid">
          <label htmlFor="one" className="block ">
            <input type="checkbox" id="one" onChange={handleQueryChange} className="m-3"/>
            Instagram</label>
          <label htmlFor="two" className="block">
            <input type="checkbox" id="two" onChange={handleQueryChange} className="m-3"/>
            LinkedIn</label>
          <label htmlFor="three" className="block">
            <input type="checkbox" id="three" onChange={handleQueryChange} className="m-3"/>
            Twitter</label>
        </div>
      </div>
    </form>
  );
}
3 Answers

You're really close with your logic and code so far so well done! I'll answer your questions in order:

  1. I have a onClick, but I'm not sure if my logic is correct to open and close the button.Also should there be a useEffect here to listen to changes?

Yep your logic to toggle the hide and show of the dropdown is spot on. The only thing is you don't have to worry about CSS there. You can use this boolean to display or hide the dropdown when the state changes.

  1. I have a onChange called selectionOptions that should let me know which platforms are selected, but it currently only shows one platform at a time, why?

Good question, the reason for this is because its firing on the change of "each" checkbox. As you change checkboxes you'll see the event fire and just show the value for the new checkbox. The checkbox itself is what the event.target refers to. Not the whole form

  1. Is there another way to create this dropdown and checkbox. Maybe a library using hooks?

I don't think you'll need to in this case. I took your code sandbox and enhanced it with just a couple additional bits of code to show how close you were!

Here it is

I'll point out a couple of changes i made:

  1. Instead of dealing with CSS I used your expanded variable to determine if I should render the dropdown. This conditional logic is on line 50.

  2. Because you want to keep track of all the different queries I changed the query variable to keep track of which queries were selected in an array. The logic for this is on line 26, but basically if the user unticks a checkbox I remove it from the array (if its there) and if they check it I add it to the array (if its not there).

I hope this helps you out!

The answer given by Code Novitiate is really good here to solve the bugs in your existing code This is a slightly different approach that simplifies the code.

Currently you are trying to rendering a select and separately rendering checkboxes. The select and its contents are never actually used, asides of visually to show the dropdown icon.

Personally, I find the native select tag hard to work with, particularly when you want to select multiple options. Most select libraries seem to create their own component out of divs and style them to look and feel like a select, which I think might be the best approach here as well

Approach

  • Conditionally render the checkboxes based on whether the dropdown has been expanded
  • Keep track of selections in local state
  • Update state whenever one of the checkboxes is clicked
  • Render selections in the dropdown header
  • Style dropdown header to look like a select
    • In my example I added a little triangle in css, but you would use something that better matches the designs you have

Example

I put together this CodePen which is hopefully useful for you

Other suggestions

  • react-select is a really good library for doing what you would like, take a look at the Multi example.
  • Given that selectionOptions never changes, there's no reason to hold that as state. You can just declare an array instead. In my example, this is the PLATFORMS constant.
  • remove <div className="absolute left-0 right-0 top-0 bottom-0" />. It looks like the effect is to create a div that covers the whole area? This gave me troubles because it interferes with some click events

All you want to have a dropdown of with checkbox for Each element. Please refer to Sandbox to understand the making of this whole dropdown.

the approach you are using is not Right and bit complex and not reusable. In react the whole idea is to create common reusable components but your code is very tightly coupled to your use-case. There is no point in creating a "Select" and then separately creating a list of checkboxes. Checkboxes should appear as a child to you dropdown and not outside it, making it very easy for you to manage dropdown and its state.

Related