skip to Main Content

So I have a DropDown component that I need to refactor to use GoChevronDown and GoChevronLeft depending upon the user clicking the Select label, I have tried this on my own and I continue to just get a blank screen:

import { useState } from "react";
import { GoChevronDown } from "react-icons/go";

function Dropdown({ options, value, onChange }) {
  const [isOpen, setIsOpen] = useState(false);

  const handleClick = () => {
    setIsOpen(!isOpen);
  };

  const handleOptionClick = (option) => {
    // CLOSE DROPDOWN
    setIsOpen(false);
    // WHAT OPTION DID THE USER CLICK ON??
    onChange(option);
  };

  const renderedOptions = options.map((option) => {
    return (
      <div
        className='hover:g-sky-100 rounded cursor-pointer p-1'
        onClick={() => handleOptionClick(option)}
        key={option.value}
      >
        {option.label}
      </div>
    );
  });

  return (
    <div className='w-48 relative'>
      <div
        className='flex justify-between items-center cursor-pointer border rounded p-2 shadow bg-white w-full'
        onClick={handleClick}
      >
        {value?.label || "Select..."} <GoChevronDown className='text-3xl' />
      </div>
      {isOpen && (
        <div className='absolute top-full border rounded p-3 shadow bg-white w-full'>
          {renderedOptions}
        </div>
      )}
    </div>
  );
}

export default Dropdown;

I thought I would add an index to the mapping function and then compare that index to isOpen.

I also developed an icon variable where I placed some span tags with both chevron icons.

I figured I had to put some of the elements in the second return statement inside the mapping function, but no matter how I do it I just get a blank screen.

So I have a minimal reproducible example here of what I attempted:

https://codesandbox.io/s/trusting-wind-f6dkd5?file=/src/components/Dropdown.js

But you will see that it does not look right.

There should be a Select… with an icon to the right and when you click on it some items dropdown, "red", "green", "blue" to be exact.

2

Answers


  1. Chosen as BEST ANSWER

    This is what I was going for, its a struggle for me writing JS in JSX sometimes, but here it is:

    return (
        <div className='w-48 relative'>
          <div
            className='flex justify-between items-center cursor-pointer border rounded p-2 shadow bg-white w-full'
            onClick={handleClick}
          >
            {value?.label || "Select..."}
            {isOpen ? (
              <GoChevronDown className='text-3xl' />
            ) : (
              <GoChevronLeft className='text-3xl' />
            )}
          </div>
          {isOpen && (
            <div className='absolute top-full border rounded p-3 shadow bg-white w-full'>
              {renderedOptions}
            </div>
          )}
        </div>
      );
    

    With this refactor, the Dropdown component will display the GoChevronDown icon when the dropdown is clicked and the GoChevronLeft icon when the dropdown is not clicked. This is what I was going for, but it was a struggle to figure it out, again writing JS in JSX can be sometimes painful for me. My apologies for any confusion.


  2. Since you’re already holding the selected item in a useState in <App>, you could use that value to check if the icon should be opened or closed.


    const isExpanded = option.value === value?.value;
    

    Since option.value hold the current option from the iteration, and value.value will hold the selected value, no need to keep track of the ‘selected index’


    Regarding OP’s comment, if you want the icon to only show it with the ‘select’, you can move it next to the select using a fragment and an if/else that will choose between value.label or Select + icon

    {
      (value) 
        ? value.label 
        : <React.Fragment>
            {"Select..."}
            "LEFT"
          </React.Fragment>
    }
    

    Small demo from your fiddle
    With some minor changes to make it work here in a SO snippet

    const { useState } = React;
    
    function Dropdown({ options, value, onChange }) {
      const [isOpen, setIsOpen] = useState(false);
    
      const handleClick = () => {
        setIsOpen(!isOpen);
      };
    
      const handleOptionClick = (option) => {
        // CLOSE DROPDOWN
        setIsOpen(false);
        // WHAT OPTION DID THE USER CLICK ON??
        onChange(option);
      };
    
      const renderedOptions = options.map((option) => {
        return (
          <div
            className="hover:g-sky-100 rounded cursor-pointer p-1"
            onClick={() => handleOptionClick(option)}
            key={option.value}
          >
            {option.label}
          </div>
        );
      });
    
      return (
        <div className="w-48 relative">
          <div
            className="flex justify-between items-center cursor-pointer border rounded p-2 shadow bg-white w-full"
            onClick={handleClick}
          >
            {
              value 
                ? value.label 
                : <React.Fragment>
                    {"Select..."}
                    "LEFT"
                  </React.Fragment>
            }
          </div>
          {isOpen && (
            <div className="absolute top-full border rounded p-3 shadow bg-white w-full">
              {renderedOptions}
            </div>
          )}
        </div>
      );
    }
    
    const Example = () => {
    
        const [selection, setSelection] = useState(null);
    
        const handleSelect = (option) => {
          setSelection(option);
        };
    
        const options = [
          { label: "Red", value: "red" },
          { label: "Green", value: "green" },
          { label: "Blue", value: "blue" }
        ];
        return (
          <Dropdown options={options} value={selection} onChange={handleSelect} />
        );
    }
    ReactDOM.render(<Example />, document.getElementById("react"));
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
    <div id="react"></div>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search