1

I'm working on a form which contains a section where users can choose the quantity and category of a concert ticket in a dropdown. This component is dynamically created and it can have more items(quantity + category). The dropdowns of the categories are related to each other. So if you choose one category the value of the other dropdowns should be set to the same selected value. But I also need the name of each category dropdown for validation.

This is how I created the category object:

const createCategory = () => {
        let cat = {}
        let nestedCat = {}
        for (let it in item) {
            for (let bt in item[it].buyertypes) {
                cat2[item[it].buyertypes[bt].id] = ''
            }
            cat[item[it].id] = nestedCat
            nestedCat = {}
        }
        return cat
    } 
    const [category, setCategory] = useState(createCategory());

This is the initial object:

{​
  9871: { 1: "", 2: "", 3: "" }
​
  9872: { 5: "", 6: "", 8: "" }
}

How I show and handle the ticket component

  const handleQuantity = e => {
      setQuantity({
          ...quantity,
          [e.target.name]: e.target.value
      });
  }

  const handleCategory = e => {
      setCategory({
          ...category,
          [e.target.name]: e.target.value
      });
 }

 const ShowData = () => {
        let ticketIem = []
        for (let it in item) {
            for (let bt in item[it].buyertypes) {
                let buyerType = item[it].buyertypes[bt]
                ticketIem.push({
                    'price': buyerType.prices, 'catId': item[it].id,
                    'qntId': item[it].buyertypes[bt].id
                })
            }
        }

        return (
            <div>
                {ticketIem.map((i, index) => (
                    <div key={index} >
                        <div>
                            <label>Number</label>
                            <select
                                value={quantity[i.qntId]}
                                onChange={handleQuantity}
                                name={i.qntId}
                            >
                                <option value={0}>0</option>
                                <option value={1}>1</option>
                            </select>
                        </div>
                        <div>
                            <label>Category</label>
                            <select
                                value={category[i.catId]}
                                onChange={handleCategory}
                                name={i.catId}
                            >
                                <option value="">Choose</option>
                                {categories.map((cat, index) => (
                                    <option key={index} value={cat.id} name={i.qntId}>
                                        {cat.description} {cat.value}
                                    </option>
                                ))}
                            </select>
                        </div>
                    </div>
                ))}
            </div>
        )
    }

After selecting an option in the dropdown the category object will be set like:

{
  9871: "11", 9872: "7"
}

but I expect:

{​
  9871: { 1: "11", 2: "11", 3: "11" }
​
  9872: { 5: "7", 6: "7", 8: "7" }
}
0

1 Answer 1

1

If you want to set all the nested properties of a specific category or quantity then you'll need to also iterate the keys of those nested properties. Use Object.keys to get an array of the nested object's keys, and reduce them into a new object with the new value set for each key.

I suggest also using a functional state update since each update depends on the existing/current state as it is shallowly copied into the next state.

const handleQuantity = (e) => {
  setQuantity((quantity) => ({
    ...quantity,
    [e.target.name]: Object.keys(quantity[e.target.name]).reduce(
      (obj, key) => ({
        ...obj,
        [key]: e.target.value
      }),
      {}
    )
  }));
};

const handleCategory = (e) => {
  setCategory((category) => ({
    ...category,
    [e.target.name]: Object.keys(category[e.target.name]).reduce(
      (obj, key) => ({
        ...obj,
        [key]: e.target.value
      }),
      {}
    )
  }));
};
Sign up to request clarification or add additional context in comments.

5 Comments

It works perfectly @DrewReese. What if I want to set each nested property to what the user selects on the dropdown?
@NimaPostanchi Is that not what the target.value value is? Or am I not understanding your follow-on question?
@DrewResse in the previous case I wanted to set all the nested properties of a specific category to a specific value so all dropdowns have the same value and the code you suggested works perfectly. Now I want to set the value of each dropdown to what the user selects(not the same value for all dropdowns). Now I want the object looks like {​ 9871: { 1: "4", 2: "3", 3: "1" } ​ 9872: { 5: "1", 6: "7", 8: "4" } } target.value gives the correct value but I can't set it to the right nested property. I hope I made it clear. Thanks in advance for your help.
@NimaPostanchi I think I need a bit more context as right now I'm not seeing what UI relates to the nested values. It looks like name={i.qntId} and name={i.catId} are the outer quantity/category keys (event.target.name), and the selected option is the value (event.target.value), so I'm unsure what you are using for the intermediate keys. Could you provide a running codesandbox I could inspect?
@DrewResse actually this use case is an extension for the previous case. Each time the user selects the number of quantity there will be shown some input fields for promotion code based on the selected number. I create this promotion code items in a nested object in the quantity event handler and give them a key name. I can't pass the correct name={}. here is running codesandbox I want to set the promotionCode object of each promotion field to e.taget.value in handleCode.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.