0

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

Code sandbox link

0

5 Answers 5

2

The current code is mutating the state invariant from arrays to objects.

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

If the state array only ever has a single element in it then use a functional state update and create a new array reference with only the single object element in it. Shallow copy the previous state's state[0] element and update the ages property by also shallow copying the array and appending the new object with the age property.

Example:

const randomAge = () => {
  const age = (Math.random() * 100).toFixed();
  setState((prev) => [
    {
      ...prev[0],
      ages: [...prev[0].ages, { age }]
    }
  ]);
};

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)

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

1 Comment

Exactly what I was looking for, I was looking to do this way only, thanx for your answer.
0

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.

1 Comment

Hey that state should be like [] array only because in the future I might need to add a new name and last name, That is why I have to use array. please check my codesand box link
0

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);
}

11 Comments

This did not worked, I have shared my code sand box please check once
@viveksingh i updated my answer with a stackblitz example
please don't introduce the pitfall of assigning newState = [...state] if state needs to be updated and the previous version of state is relevant, you are required to use prevState of setState(prevstate=> ...)
He did not specify how his state needs to be or anything of that sort, just that he wanted it to output a specific format. That's what the answer does, it is a minimal working example for what he needs and maintains immutability since we never touch the original state directly.
I will let him expand further on this to make sure he filters out the correct user this needs to be applied to. The idea that a user has an array of ages also makes no sense conceptually. You have 1 age, not multiple ages
|
0

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.

Comments

-1

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

1 Comment

This should probably be a comment instead of an answer. Make sure that any answers you post are actual attempts to solve the question.

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.