Home > Software engineering >  React Js change array element
React Js change array element

Time:10-04

I have a list of elements in a react js application.

import "./styles.css";
import React from 'react';
const carsData = [
  {name: "first car", id: 1, meta: [{id:1, title:'first meta'}]},
  {name: "second car", id: 2, meta: [{id:2, title:'second meta'}]},
  {name: "third car", id: 3, meta: [{id:4, title:'last meta'}]},
]

export default function App({cars = carsData}) {
  const [carsState, setCarsState] = React.useState(cars) 
  const newItem = {name: "first car", id: 1, meta: [{id:10, title:'added meta'}]}
  

    const click = () => {
      setCarsState([...carsState, newItem])
    }
  return (
    <div className="App">
      <button onClick={click}>click</button>
      {
        carsState.map(c => {
          return <p key={c.name}>{c.name} - meta:
           {c.meta.map((m, k) => <span key={k}>{m.title}</span>)}</p>
        })
      }
    </div>
  );
}

If the user clicks on the button, it should change the first item from the array. Now if i click on the button, the new item is added at the end of the list, but it should change the first item, because the id is the same.
Why the code doe not work and how to change to get the expected result?
demo: https://codesandbox.io/s/ecstatic-sound-rkgbe?file=/src/App.tsx:56-64

CodePudding user response:

the problem was in this line:

setCarsState([...carsState, newItem])

you have to filter out those items with id that is not equal to newItem.id then add the newItem like:

setCarsState([...carsState.filter(e => e.id !==newItem.id),newItem]);

import "./styles.css";
import React from "react";
const carsData = [
  { name: "first car", id: 1, meta: [{ id: 1, title: "first meta" }] },
  { name: "second car", id: 2, meta: [{ id: 2, title: "second meta" }] },
  { name: "third car", id: 3, meta: [{ id: 4, title: "last meta" }] }
];

export default function App({ cars = carsData }) {
  const [carsState, setCarsState] = React.useState(cars);
  const newItem = {
    name: "first car",
    id: 1,
    meta: [{ id: 10, title: "added meta" }]
  };

  const click = () => {
    setCarsState([...carsState.filter(e => e.id !==newItem.id),newItem]);
  };
  return (
    <div className="App">
      <button onClick={click}>click</button>
      {carsState.map((c) => {
        return (
          <p key={c.name}>
            {c.name} - meta:
            {c.meta.map((m, k) => (
              <span key={k}>{m.title}</span>
            ))}
          </p>
        );
      })}
    </div>
  );
}

here is the sandbox

CodePudding user response:

Okay so your code -as you said- will simply push to the end of the array. Because you haven't really told it to do otherwise.

If you're looking to replace the old item with the new item if they have the same id value. I'd suggest changing the initial array to an object where the key to each value is the id of an item.

I highly recommend you do this if you'll interact with your items by their id's. An array will soon become inefficient once you scale up your operation to include many items.

import React from 'react';

//notice we changed carsData from an array into an object. Notice 
//the keys are equal to each item's id
const carsData = {
  1:{name: "first car", id: 1, meta: [{id:1, title:'first meta'}]},
  2:{name: "second car", id: 2, meta: [{id:2, title:'second meta'}]},
  3:{name: "third car", id: 3, meta: [{id:4, title:'last meta'}]},
}

export default function App({cars = carsData}) {
  const [carsState, setCarsState] = React.useState(cars) 
  const newItem = {name: "first car", id: 1, meta: [{id:10, title:'added meta'}]}
  

    const click = () => {
      //We use the id of the new item as the key, 
      //this will enable us to replace the value of the old item because 
      //objects can't have the same key twice.
      setCarsState({...carsState, [newItem.id]: newItem})
      console.log(carsState)
    }
  return (
    <div className="App">
      <button onClick={click}>click</button>
      {
        //instead of mapping the array, now we map the
        //values of our object by using the method 
        //Object.values(carsState) which returns an array
        //of values from our object.
        Object.values(carsState).map(c => {
          return <p key={c.name}>{c.name} - meta:
           {c.meta.map((m, k) => <span key={k}>{m.title}</span>)}</p>
        })
      }
    </div>
  );
}

Using this solution will permit you to fetch each item by its id. By simply typing

carsData[id_of_item]

If your carsData comes as an array from DB you can just map it into an object

var carsDataObject = {}
cars.map(item => carsDataObject[item.id] = item)
const [carsState, setCarsState] = React.useState(carsDataObject) 

And then you can use carsDataObject instead of the carsData array

  • Related