Mark Scholes
Mark Scholes

Reputation: 85

How do I add select options from an object

I am trying to create a dropdown list based on a object I have. I can't think of a way to do it that's not completely convoluted and the solutions I have come up with don't work either. I'm new to react and stackoverflow so I am not sure if just asking for advice is seen as okay. Forgive me if it's not. I am building it with React. Is there a way to do this that I am missing. Again sorry if you are not supposed to ask for help here

I have created the component as follows

const HardwearForm = ({isActive, Products, outputObj, setOutputObj}) => {

const handleSelect = e => {
    let newParentNode = e.target;
    let selectedNumber = newParentNode.options.selectedIndex;
    let choice = newParentNode.options[selectedNumber].value;               
    setOutputObj(outputObj[newParentNode.id] = choice);        
    console.log(outputObj)        
}        

useEffect(() => { 
    let parentNode = document.getElementById("make");
    Object.keys(Products[isActive]["hardwear"]).forEach(key => {            
    let newOption = document.createElement('option');
    newOption.value = key;
    newOption.innerText = key;
    parentNode.appendChild(newOption);
    })
},[isActive, Products]); 

return (
    <div>
    <select name="make" className="hardwear" id="make" onChange={handleSelect}>
        <option value="*">Manufacturer</option>
    </select>
    <select name="model" className="hardwear" id="model"></select>
    {isActive === "handset"|| isActive==="tablet"?
    <>
    <select name="storage" className="hardwear" id="storage""></select></>:""}
    {isActive === "handset"|| isActive==="tablet" ||isActive=== "watch"?
    <>
    <select name="color" className="hardwear"></select></>:""}
    {isActive === "handset"|| isActive==="watch"?
    <>
)

The basic idea is I have a menu with a few buttons. Those buttons update the isActive state, which then causes the useEffect to run and populate the first select with the options. That works fine. I've then set it up so once a selection in the first dropdown is chosen it fires the handleSelect function, which does the same thing for the next select. I've set up a empty object to store all the options. setOutputObj(outputObj[newParentNode.id] = choice); ands a key value pair to that object. The problem is, if you then try and change the selected option it errors out "TypeError: Cannot create property 'make' on string 'Huawei'".

Here is an example of the date structure I am using.

"Fairphone": {
        "3+": {
            "color": ["Black"],
            "storage": ["64GB"], 
        }
    },
    "Doro": {
        "8050": {
            "color": ["Black"],
            "storage": ["16GB"], 
        }
    },
    "Mobiwire": {
        "Smart N12": {
            "color": ["Dark Metallic Blue"],
            "storage": ["16GB"], 
        }
    },

Upvotes: 1

Views: 1414

Answers (1)

coot3
coot3

Reputation: 632

I'm a bit confused about your data structure and what you are trying to achieve, but in general, to dynamically set options of a select input from an object I think it's better to use array.map rather than to try to modify the DOM directly.

For example: https://codesandbox.io/s/compassionate-buck-8sir5?file=/src/App.js

import { useState } from "react";
import "./styles.css";

const productsByManufacturers = {
  "Manufacturer 1": ["product 1", "product 2", "product 3"],
  "Manufacturer 2": ["product 4", "product 5"]
};

export default function App() {
  const [manufacturer, setManufacturer] = useState("");

  return (
    <div className="App" style={{ display: "flex", flexDirection: "column" }}>
      <label>Select Manufacturer:</label>
      <select
        value={manufacturer}
        onChange={(e) => setManufacturer(e.target.value)}
      >
        <option value="" disabled>
          Select a manufacturer
        </option>
        {Object.keys(productsByManufacturers).map((manufacturer) => (
          <option value={manufacturer}>{manufacturer}</option>
        ))}
      </select>

      {manufacturer !== "" && (
        <div
          style={{ marginTop: 20, display: "flex", flexDirection: "column" }}
        >
          <label>Select Product:</label>
          <select>
            {productsByManufacturers[manufacturer].map((product) => (
              <option value={product}>{product}</option>
            ))}
          </select>
        </div>
      )}
    </div>
  );
}

Upvotes: 1

Related Questions