El ejemplo y el comportamiento observado están aquí:
Ejemplo de réplica
Tengo una matriz de objetos, cada uno con datos que se pasan al nuevo componente en #each loop. Parece algo bastante estándar para tener.
Cada componente tiene un botón asociado con su variable de estado, "isDescVisible", que dice "mostrar/ocultar más detalles". Hasta aquí todo bien. Hacemos clic en "mostrar detalles" en el "primer" componente, obtenemos detalles del primer componente.
Pero luego, agregamos un objeto más a la matriz de datos. Aún así, es algo bastante común que suceda. Y se renderiza el nuevo componente. Pero de alguna manera, el estado (que muestra detalles o no) de todos los componentes (tanto antiguos como nuevos) está en mal estado. El nuevo componente hereda el estado del anterior "superior", y así sucesivamente.
Pregunta: ¿por qué? segunda pregunta: ¿cómo evitar eso?
Podría haber imaginado que después de mutar la matriz, todos los componentes se destruyen y se vuelven a renderizar. Pero en ese caso, no deberíamos haber visto los detalles mostrados para ninguno de ellos.
Para aquellos que no quieren ir a repl, código a continuación:
App.svelte:
<script>
import Entry from './Entry.svelte'
let base = [
{title: 'First title', desc: 'First description'},
{title: 'Second title', desc: 'Second description'}
]
function updateBase() {
const newEntry = {title: 'Third title', desc: 'Third description'}
base = [newEntry,...base]
}
</script>
<button on:click={updateBase}>Add me!</button>
{#each base as entry}
<Entry {entry} />
{/each}
Entry.svelte:
<script>
export let entry
let isDescVisible = false
</script>
<p>
{entry.title}
</p>
<button on:click={()=> isDescVisible = true}>
Show Description
</button>
{#if isDescVisible}
<h2>
I am visible! My description is {entry.desc}
</h2>
{/if}
Solución del problema
Asigne a cada elemento un ID y utilícelo como clave en el #each
bloque:
<script>
import Entry from './Entry.svelte'
let base = [
{title: 'First title', desc: 'First description', id: 1},
{title: 'Second title', desc: 'Second description', id: 2}
]
function updateBase() {
const newEntry = {title: 'Third title', desc: 'Third description', id: 3}
base = [newEntry,...base]
}
</script>
<button on:click={updateBase}>Add me!</button>
<!-- The each loop uses the property in parentheses as the key -->
{#each base as entry (entry.id)}
<Entry {entry} />
{/each}
Al proporcionar una clave, está ayudando a Svelte a determinar qué nodo DOM cambiar cuando base
se actualiza. Sin una clave, Svelte agregará y eliminará elementos al final de la lista de forma predeterminada y luego actualizará los valores que hayan cambiado. Así que empezamos con esto...
... y agregamos un elemento a la matriz, así que queremos esto...
Sin la clave, Svelte no sabe que estamos agregando un elemento al frente de la lista. Esa es solo una forma de llegar al estado final. Otra forma (y la predeterminada por Svelte) es actualizar el título de (1) para que sea "Tercer título", actualizar el título de (2) para que sea "Primer título" y agregar un nuevo elemento que tenga "Segundo título". ". Debido a que (1) solo tiene los accesorios actualizados y no se recrean, se conserva su estado interno abierto/cerrado.
Al agregar una clave, Svelte sabe que el elemento al frente de la lista es un elemento nuevo y creará un nuevo nodo allí en lugar de reutilizar el anterior.
Tan Li Hau tiene una buena explicación visual de esto en Twitter.
tl; dr Siempre debe proporcionar una clave en un #each
bucle si va a modificar la lista de alguna manera.
No hay comentarios:
Publicar un comentario