GOOGLE ADS

lunes, 25 de abril de 2022

Svelte: estado de los componentes renderizados con #each loop

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 #eachbloque:

<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 basese 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...

  • Primer título (estado abierto)

  • segundo titulo

  • ... y agregamos un elemento a la matriz, así que queremos esto...

  • tercer titulo

  • primer titulo

  • segundo titulo

  • 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 #eachbucle si va a modificar la lista de alguna manera.

    No hay comentarios:

    Publicar un comentario

    Regla de Firestore para acceder a la generación de subcolección Permisos faltantes o insuficientes

    Tengo problemas con las reglas de Firestore para permitir el acceso a algunos recursos en una subcolección. Tengo algunos requests document...