Tengo una lista de elementos en una aplicación reaccionar js.
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> ); } Si el usuario hace clic en el botón, debería cambiar el primer elemento de la matriz. Ahora, si hago clic en el botón, el nuevo elemento se agrega al final de la lista, pero debería cambiar el primer elemento, porque la identificación es la misma.
¿Por qué el código no funciona y cómo cambiar para obtener el resultado esperado?
demostración: https://codesandbox.io/s/ecstatic-sound-rkgbe?file=/src/App.tsx:56-64
el problema estaba en esta linea:
setCarsState([...carsState, newItem]) debe filtrar los elementos con una identificación que no es igual a newItem.id y luego agregar el newItem como:
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> ); }aquí está la caja de arena
De acuerdo, tu código, como dijiste, simplemente push hasta el final de la array . Porque realmente no le has dicho que haga lo contrario.
Si está buscando reemplazar el artículo anterior con el artículo nuevo si tienen el mismo valor de identificación. Sugeriría cambiar la array inicial a un object donde la key de cada value es la identificación de un elemento.
Le recomiendo que haga esto si va a interactuar con sus artículos por sus ID. Una matriz pronto se volverá ineficiente una vez que amplíe su operación para incluir muchos elementos.
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> ); }El uso de esta solución le permitirá obtener cada elemento por su id. Simplemente escribiendo
carsData[id_of_item] Si su carsData viene como una matriz de DB, puede asignarlo a un objeto
var carsDataObject = {} cars.map(item => carsDataObject[item.id] = item) const [carsState, setCarsState] = React.useState(carsDataObject) Y luego puede usar carsDataObject en lugar de la matriz carsData