Tengo un componente principal que se mapea sobre un estado de list usando un componente de Input
import "./styles.css"; import { useState, useEffect } from "react"; import Input from "./Input"; export default function App() { const [list, setList] = useState([{}]); useEffect(() => { console.log("list", list); }, [list]); return ( <div className="App"> <h1>Hello CodeSandbox</h1> <h2>Start editing to see some magic happen!</h2> {list.map((_, i) => ( <div key={i}> <Input id={i} setList={setList} /> </div> ))} </div> ); } Enviar cada Input deja otra Input disponible para enviar. Sin embargo, cuando elimino cada Input , veo que el objeto de datos se ha ido pero la interfaz de usuario no refleja esto.
import { useState } from "react"; const Input = ({ setList, id }) => { const [value, setValue] = useState(""); const [submitted, setSubmitted] = useState(false); const handleChange = (event) => { const result = event.target.value.replace(/\D/g, ""); setValue(result); }; const handleSubmit = () => { setList((list) => [...list, { id: id, total: value }]); setSubmitted(true); }; const handleDelete = () => { console.log("id", id); setList((list) => list.filter((item) => item.id !== id)); }; return ( <div> <input value={new Intl.NumberFormat("ko-KR", { style: "currency", currency: "KRW" }).format(Number(value))} onChange={handleChange} type="text" /> {submitted ? ( <button disabled={value.length === 0} onClick={handleDelete}> Delete </button> ) : ( <button disabled={value.length === 0} onClick={handleSubmit}> Submit! </button> )} </div> ); }; export default Input;Sospecho que esto es porque
const [list, setList] = useState([{}]) Se inicializa con un objeto vacío, y eso está causando el problema. Sin embargo, necesito al menos un objeto dentro del estado de la list para poder tener el componente de Input inicial para el envío.
¿Cómo elimino correctamente mi componente de Input ?
Mis códigosandbox
Tu problema es con tu key .
<div key={i}></div> React usa el accesorio key para ayudar a identificar qué elementos se han cambiado. Las claves deben permanecer iguales para cada elemento en su matriz a través de las nuevas representaciones, de modo que React pueda identificar cuándo un elemento se actualizó en función de un cambio de estado. A medida que elimina elementos de su estado, el índice no sirve como una clave estable que se asocia de manera única con su objeto (ya que los objetos cambian a índices más bajos cuando se elimina uno). En cambio, es mejor agregar algún tipo de id asociada con los elementos que representa:
const [list, setList] = useState([{ id: 0}]); ... {list.map(({ id }) => ( <div key={id}> <Input id={id} setList={setList} /> </div> ))} ... Luego, en su componente donde agrega sus objetos, puede crear una nueva id (que no cambia) para cada nuevo objeto. A continuación, he usado Math.random() , pero debería usar algo más confiable que eso (por ejemplo: uuidv4 ).
setList((list) => [...list, { id: Math.random(), total: value }]); También debe ver cómo está agregando nuevos valores. La entrada "enviar" solo aparece debido al nuevo objeto que está agregando a su lista: { id: Math.random(), total: value } . En su lugar, haría que la App mostrara una entrada que es la principal responsable de agregar elementos a su estado de list , y luego hacer que cada Input que se represente dentro del .map() se encargue de la funcionalidad "eliminar".
Esto sucede porque usaste el índice como clave en el mapa.
Puedes leer más sobre esto aquí .
Para resumir, por ejemplo, tiene 3 elementos, elimina el segundo que tiene la clave "2", luego su tercer elemento recibe una clave "2" y reacciona pensando que este es el mismo elemento.
Actualicé su ejemplo con una plantilla de elemento que tiene una identificación temporal establecida en "elemento nuevo" y usé la identificación como clave.
También creo una identificación para cada elemento nuevo en la función handleSubmit, por lo que su identificación temporal nunca se convirtió en una real.
Aquí está el código actualizado. Eliminé un código que no se modificó.
componente de la aplicación
const TEMPLATE = { id: "new item", value: 0, } export default function App() { const [list, setList] = useState([{...TEMPLATE}]); return ( <div className="App"> {list.map((item) => ( <div key={item.id}> <Input id={item.id} setList={setList} /> </div> ))} </div> ); }Y aquí está el componente de entrada. Le gustaría generar Id de alguna otra manera.
const Input = ({ setList, id }) => { ... const handleSubmit = () => { setList((list) => [...list, { id: Math.random(), total: value }]); setSubmitted(true); }; ... };