Home > Net >  List items with inputs - how to add list object to an array when input has value?
List items with inputs - how to add list object to an array when input has value?

Time:01-13

I have created a list of objects and each list item has input within:

{flowers_data?.map((flower) => {
   return (
      <>
         <div className={classes.Nested_Flower_Container} key={flower.id}>
            <div className={classes.Nested_Flower_Name}>
               {flower.name}
            </div>
         <div className={classes.Nested_Flower_Input} style={{ marginRight: '0.2em' }}>
            <TextField
              id="Amount"
              label="Amount"
              variant="outlined"
              size="small"
              type="number"
              onChange={(e) => {
                 setAmount(e.target.value);
                 handleAddList(e.target.value, flower);
              }}
              className={classes_2.root}
            />
         </div>
      </div>
  </>)
})}

How can I add an object that has a value in input to an array? I tried to do this using a function that I created, but each time I change one element's target.value and move on to the next item to change its input value, there is only one element in the array with the latest target.value. And after modifying the inputs, when I try to output the values outside that function with e.g. a button, the add_flowers_tab array is empty.

handleAddList function:

let temp_flower: Flower;
let add_flowers_tab: Flower[] = [];

    const handleAddList = (targetValue: string, flower: Flower) => {

        temp_flower = {
            "id": flower.id,
            "name": flower.name,
            "price": flower.price,
            "amount": Number(targetValue),
            "creation_date": flower.creation_date
        }

        if (targetValue === '') {
            /* Delete flower when input is empty */
            add_flowers_tab.forEach(tabFlower => {
                if (tabFlower.id === temp_flower.id) {
                    const indexOfDelete = add_flowers_tab.indexOf(tabFlower);
                    add_flowers_tab.splice(indexOfDelete, 1);
                }
            })
        }
        if (targetValue !== '') {
            /* Add flower to tab when input has value */
            if (add_flowers_tab.length > 0) {
                /* When input changes, delete flower with old input value and add the new one */
                add_flowers_tab.forEach(tabFlower => {
                    if (tabFlower.id === temp_flower.id) {
                        const indexOfDelete = add_flowers_tab.indexOf(tabFlower);
                        add_flowers_tab.splice(indexOfDelete, 1);
                        add_flowers_tab.push(temp_flower);
                    }
                })
            }
            else {
                /* Add new flower as a first element in the array */
                add_flowers_tab.push(temp_flower);
            }

            /* 
               Displays an array with only the most recently added temp_flower, even though 
               several inputs in list have values
            */

            console.log(add_flowers_tab);
        }
    }

CodePudding user response:

You can have one React state, which has an array of objects that you enter through your "mapped" component.

const [tmpAmount, setTmpAmount] = React.useState<{flower:Flower,amount:number}[]>([])
// fill the temporary state with generated flowers only once
React.useEffect(()=>{
    setTmpAmount(flowers_data.map((flwr,index) =>{flower:flwr, amount:0}))
},[])

and replace the onChange()

onChange={(e) => {
// find existing flower to edit the amount of it
    let indexToChange = flowers_data.findIndex((element) => element.id === flower.id)
    setAmount2((prev) => 
        [
            ...prev.slice(0, indexToChange),
            {
                ...prev[indexToChange],
                amount: e.target.value,
            },
            ...prev.slice(indexToChange   1),
        ]);
}

You can also check if the array changes and print it:

React.useEffect(()=>console.log(tmpAmount),[tmpAmount])

CodePudding user response:

Here is the minimum solution for generating a list on Inputs that each update a single element in a list on state.

export const ManyInputs = ({inputs}) => {
  const [list, setList] = useState(inputs);

  const setIndividual = (value, id) =>
    // Update a single field in the object in the list that matches the given id
    setList(list.map(l => l.id === id ? ({...l, value}) : l));

  return list.map(i =>
    // Send the id back to help match the object
    <TextField onChange={(e) => setIndividual(e.target.value, i.id)} />
  );
};
  •  Tags:  
  • Related