3

I have a small react project which contains a form, I am trying to get multiple values from a checkbox, but everytime i click an item (checkbox) for the first time, it starts out as null, then when i click the another item, It stores the first Item. I would like to store multiple Items from a checkbox. Here is the code below.

import { useState } from "react";

const Toppings = () => {

    const [toppings, setToppings] = useState(null)
    const [toppingsArray, setToppingsArray] = useState([])

    function handleCheck(event) {
        if (event.target.checked) {
            setToppings({
                "type": event.target.id,
                "price": event.target.value
            })

            console.log("toppings: ", toppings)
            toppingsArray.push(toppings)
            setToppingsArray([toppings])
            console.log("toppings array: ", toppingsArray)
        }
    }

    return (
        <>
            <h2>you can select multiple</h2>
            <form>
                <label htmlFor="pepperoni">
                    <input onChange={handleCheck} type="checkbox" name="pepperoni" id="pepperoni" value={300} />
                    pepperoni
                </label>
                <label htmlFor="cheese">
                    <input onChange={handleCheck} type="checkbox" name="cheese" id="cheese" value={200} />
                    cheese
                </label>
                <label htmlFor="onions">
                    <input onChange={handleCheck} type="checkbox" name="onions" id="onions" value={100} />
                    onions
                </label>
            </form>
            <p>you have selected {JSON.stringify(toppings)}</p>
            <p>you have {JSON.stringify(toppingsArray)}</p>
        </>
    )
}

export default Toppings[enter image description here][1];

3 Answers 3

1

Get rid of the line that says toppingsArray.push(toppings) since the toppingsArray is in state so it is immutable

Add a line that change the setToppingsArray line to

setToppingsArray(toppingsArray=>[...toppingsArray, toppings])

The useState hook can be used as a function that has the first parameter give the initial state. Then we just take that state and spread into into an array and add the new thing


There is also no reason to store the initial toppings in state since it has no interaction with the dom. It can be a js const

You should also have a checked attribute on the checkboxes that checks if a topping in is the toppings array

Sign up to request clarification or add additional context in comments.

Comments

1

Please use this code.

function handleCheck(event) {
  const item = {
    "type": event.target.id,
    "price": event.target.value
  };
  if (event.target.checked) {
    setToppings(item);
    if(toppingsArray.map(val => val["type"]).indexOf(item["type"]) == -1) setToppingsArray([...toppingsArray, item]);
  } else {
    setToppingsArray(toppingsArray.filter(val => val["type"] != item["type"]));
  }
}

Comments

1

There are multiple things that are not right in your code.

  1. setState is an async operation, which means the console.log("toppings: ", toppings) right after the setState will show you the stale state.
  2. In the next line you are pushing the toppings into the toppingsArray but the toppings contain the stale data.
  3. Next line you have setToppingsArray([toppings]), which is wrong as well because you have pushed the data into toppingsArray so you need to update the state by passing that array, like this setToppingsArray(toppingsArray).
  4. Also, while pushing the toppings into the array, you need to check if the toppings already exist or not.

To solve all these problems you could do something like this inside your handleCheck function:

  function handleCheck(event) {
    const newToppings = {
      type: event.target.id,
      price: event.target.value,
    };
    if (event.target.checked) {
      setToppings(newToppings);
      toppingsArray.findIndex(top => top.type === newToppings.type) < 0 &&
        setToppingsArray([...toppingsArray, newToppings]);
    } else {
      setToppingsArray(toppingsArray.filter(top => top.type !== newToppings.type));
    }
  }

5 Comments

because setState is async. say you wanted to get the length of the toppings array everytime you check or uncheck, how would you get the right data?
Do you want to check the length of toppingsArray before updating the state or after updating the state?
After updating state. Also if you have experience with angular, please check this out stackoverflow.com/questions/68926840/…
If you want to check the length of the toppingsArray, you don't really need to do anything special. You can just simply use toppingsArray.length anywhere in your component. Sorry, I don't have any experience in Angular.
appreciate the help. If anyone has experience with angular, please refer

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.