Tengo dos componentes, un padre y un hijo. Los componentes principales mantienen una lista de elementos secundarios en estado y la representan mediante el método de mapa.
El padre:
import { useState } from "react";
import Child from "./Child";
export default function Parent(){
const [counter, setCounter] = useState(0)
const [childKey, setChildKey] = useState(0);
const [children, setChildren] = useState([]);
function addChild(){
setChildren([...children, <Child counter={counter} index={childKey}/>]);
setChildKey(childKey + 1);
}
return (
<>
<h2>The parent component</h2>
<p>The counter is set to {counter}</p>
<p><button onClick={() => {setCounter(counter + 1)}}>Increment</button></p>
<p><button onClick={addChild}>Add child</button></p>
{children.map((c, i) => {
return (
<div key={i}>{c}</div>
);
})}
</>
);
}
El niño:
export default function Child({counter, index}){
return (
<>
<p>Child {index}: the counter is set to {counter}</p>
</>
);
}
Al hacer clic en el botón "agregar niño", se agrega un niño a la matriz de niños.
Al hacer clic en el botón "incrementar", se actualiza el contador en el padre, pero el contador en los hijos no cambia.
¿Hay alguna manera de volver a representar a los niños cuando el contador aumenta? (mientras que es preferible mantener a los niños en una matriz)
setChildren([...children, <Child counter={counter} index={childKey}/>]);
No ponga elementos en estado. Hace que sea muy fácil tener errores como el que tienes ahora. Efectivamente, ha "bloqueado" cuáles son los accesorios para el niño, por lo que los cambios en el contador no surtirán efecto.
En su lugar, simplemente almacene los datos mínimos que se necesitan para crear los elementos y luego cree esos elementos durante el renderizado. En su caso, creo que solo necesita un número que indique cuántos niños representar. Convenientemente, ya tienes eso en childKey , así que recomiendo:
export default function Parent(){
const [counter, setCounter] = useState(0)
const [childKey, setChildKey] = useState(0);
const children = [];
for (let i = 1; i <= childKey; i++) {
children.push((
<div key={i}>
<Child counter={counter} index={i} />
</div>
));
}
function addChild(){
setChildKey(childKey + 1);
}
return (
<>
<h2>The parent component</h2>
<p>The counter is set to {counter}</p>
<p><button onClick={() => {setCounter(counter + 1)}}>Increment</button></p>
<p><button onClick={addChild}>Add child</button></p>
{children}
</>
);
}
Tal vez childKey podría cambiarse de nombre a childCount .
Cuando tu lo hagas:
setChildren([...children, <Child counter={counter} index={childKey}/>]);
Le dices a React que agregue el elemento <Child counter={counter} index={childKey}/> al estado de los children . Crea un elemento con el counter de accesorios y childKey con los valores que tienen en el momento en que los agrega al estado. Volver a representar estos elementos con un valor diferente para childKey no tendrá ningún impacto.
Dependiendo de lo que quieras hacer, hay muchas soluciones. Si desea renderizar Child con el valor de counter en el momento de volver a renderizar, haría algo como esto:
export default function Parent(){
const [counter, setCounter] = useState(0)
const [childKey, setChildKey] = useState(0);
const [children, setChildren] = useState([]);
function addChild(){
setChildren([...children, ({ counter }) => <Child counter={counter} index={childKey}/>]);
setChildKey(childKey + 1);
}
return (
<>
<h2>The parent component</h2>
<p>The counter is set to {counter}</p>
<p><button onClick={() => {setCounter(counter + 1)}}>Increment</button></p>
<p><button onClick={addChild}>Add child</button></p>
{children.map((childFn, i) => {
return (
<div key={i}>{childFn({ counter })}</div>
);
})}
</>
);
}
O puede ser más simple (y menos propenso a errores):
export default function Parent(){
const [counter, setCounter] = useState(0)
const [childKey, setChildKey] = useState(0);
const [children, setChildren] = useState([]);
function addChild(){
setChildren([...children, childKey]);
setChildKey(childKey + 1);
}
return (
<>
<h2>The parent component</h2>
<p>The counter is set to {counter}</p>
<p><button onClick={() => {setCounter(counter + 1)}}>Increment</button></p>
<p><button onClick={addChild}>Add child</button></p>
{children.map((childKey, i) => {
return (
<div key={i}><Child counter={counter} index={childKey} /></div>
);
})}
</>
);
}
Sí, como otras respuestas, mantener los componentes en estado no es una buena idea, bien explicado por Nicholas arriba
Misma respuesta pero diferente estilo de rendering 😛
Acabo de actualizar Parent.js como se muestra a continuación
import { useState } from "react";
import Child from "./Child";
export default function Parent() {
const [counter, setCounter] = useState(0);
const [childKey, setChildKey] = useState(0);
function addChild() {
setChildKey(childKey + 1);
}
return (
<>
<h2>The parent component</h2>
<p>The counter is set to {counter}</p>
<p>
<button
onClick={() => {
setCounter(counter + 1);
}}
>
Increment
</button>
</p>
<p>
<button onClick={addChild}>Add child</button>
</p>
{/* create an array of length of childKey number */}
{Array.from({ length: childKey }, (_, i) => {
return <Child key={i} counter={counter} index={i} />;
})}
</>
);
}