Home > database >  How to push a new element to nested array react hooks
How to push a new element to nested array react hooks

Time:09-09

I Am trying to push a new element to my nested array but the issue I am facing is It is creating new array every time, I know this is very silly but I tried to google but not got the answer.

What i am doing

  • Below is my state

     const [state, setState] = useState([
       {
         name: "test",
         lname: "lname",
         ages: [
           {
             age: 30
           }
         ]
       }
     ]);
    
  • What I am trying to do on click is

     const randomAge = () => {
       let rg = ((Math.random() * 1000) / 10).toFixed();
       setState({ ...state, ages: { ...state.ages, age: rg } });
     };
    

But it is returning very wrong output, shown as below

{
  "0": {
    "name": "test",
    "lname": "lname",
    "ages": [
      {
        "age": 30
      }
    ]
  },
  "ages": {
    "rg": "34"
  }
}

Output I want is like below

[
  {
    "name": "test",
    "lname": "lname",
    "ages": [
      {
        "age": 30
      },
      {
        "age": 50
      },
      {
        "age": 60
      }
    ]
  }
]

I know I am doing a very silly mistake somewhere, but right now I am not abe to find

I am just trying to push a new random age to my ages array on click of button

Edit how-to-push-a-new-element-to-nested-array-react-hooks

If you are eventually wanting to handle multiple object elements in the state array then I suggest adding a GUID to each object and pass this to the randomAge callback so it can correctly update the correct state.

Example:

import React, { useState } from "react";
import { nanoid } from "nanoid";

export default function App() {
  const [state, setState] = useState([
    {
      id: nanoid(),
      name: "test1",
      lname: "lname1",
      ages: []
    },
    {
      id: nanoid(),
      name: "test2",
      lname: "lname2",
      ages: []
    }
  ]);

  const randomAge = (id) => () => {
    const age = (Math.random() * 100).toFixed();
    setState((prev) =>
      prev.map((el) =>
        el.id === id
          ? {
              ...el,
              ages: el.ages.concat({ age })
            }
          : el
      )
    );
  };

  return (
    <div className="App">
      {state.map((li, ind) => {
        return (
          <React.Fragment key={li.id}>
            <div>{li.name}</div>
            <div>{li.lname}</div>
            <br />
            {li.ages.map((d) => <div>{d.age}</div>)}
            <button onClick={randomAge(li.id)}>Add random age</button>
          </React.Fragment>
        );
      })}
    </div>
  );
}

Edit how-to-push-a-new-element-to-nested-array-react-hooks (forked)

CodePudding user response:

i am not sure but, you use array in start state ages: [ {age: 30} ] but in setState setState({ ...state, ages: { ...state.ages, age: rg } }); you use a object instead of Array

CodePudding user response:

Your initial setState() should be an object not an array.

const [state, setState] = useState(
  {
    name: "test",
    lname: "lname",
    ages: [
      {
        age: 30
      }
    ]
  }
);

As per the function randomAge try the following

const randomAge = () => {
 let rg = ((Math.random() * 1000) / 10).toFixed();
     setState({ 
      ...state,
        ages: [ 
          ...state.ages,
          {
            age : rg
          }
        ] 
      });
    };

You'll see that ages within the state object has been changed to an Array. With the spread operator you're copying the previous state ages array into it. This should be smaller objects from your code and lastly you're creating another object with age : rg as the only pair value.

It might be better to keep ages as an array and simply push the rg value into it instead of creating objects. Unless you're adding multiple KEY:VALUE pairs.

CodePudding user response:

Couple things of note:

  • .ToFixed() returns a string and not a number type
  • You want your setState to look like { ...state, ages: [ ...state.ages, {age: rg} ] } this is because ages is an array, and so you need to use the spread operator with the array syntax

Edit:

https://stackblitz.com/edit/react-jkgjf4?file=src/App.js

Working stackblitz, in a nutshell your object is an array so we need to treat it as such and loop over it as we are creating a new object. This way it is easier to update it than trying to use spread operators as it can quickly get very ugly.

const [state, updateState] = useState([
{
  name: 'test',
  lname: 'lname',
  ages: [
    {
      age: 30,
    },
  ],
},
]);

 function update() {
//turning it into an int
let rg = Number.parseInt(((Math.random() * 1000) / 10).toFixed());

let newState = [...state];
newState.forEach((x) => x.ages.push({ age: rg }));

console.log('new State: ', newState);
updateState(newState);
}

CodePudding user response:

Just change your randomAge function like this.

  const randomAge = () => {
    const rg = ((Math.random() * 1000) / 10).toFixed();
    const newAgesArray = state[0].ages;
    newAgesArray.push({ age: rg });
    setState([...state, { ages: newAgesArray }]);
  };

Considering only one item in the whole array, I used an index 0.

Also const newAgesArray = state[0].ages doesn't make a whole new copy of the array, it just creates a reference. It works but it's not ideal when working with state. I recommend you to do some shallow or deep copy instead - DYOR.

CodePudding user response:

your sample ist not entire clear to mee, but you need [] to define ages as an array of elements. like:

use prev "prevState" param. not the reference state. state is not guaranteed to be up to date.

  • consider 12 your random number:
  • we update the first element (index 0) in your array of people

hope this helps:

setState((prev) => ([...prev.filter((a,i) => i !== 0), {...prev[0], ages: [...prev[0].ages, {age:12}]} ]))

or filter the element you wanna edit with a selector based on the element:

setState((prev) => ([...prev.filter((a) => a.name !== "test"), {...prev.find(a=> a.name === "test"), ages: [...prev.find(a=> a.name === "test").ages, {age:12}]} ]))

does the same. some of this complexity is owned to the fact, that your base element is an array.

you can of course build sub functions to add readability. but the basics won't change.

  • Related