Saltearse al contenido

Anidación (Nesting)

Como muchos otros frameworks basados en componentes, los componentes de Livewire son anidables - lo que significa que un componente puede renderizar múltiples componentes dentro de sí mismo.

Sin embargo, debido a que el sistema de anidamiento de Livewire está construido de manera diferente a otros frameworks, hay ciertas implicaciones y restricciones que es importante tener en cuenta.

Cada componente es una isla

En Livewire, cada componente de una página rastrea su estado y realiza actualizaciones independientemente de otros componentes.

Por ejemplo, considere los siguientes componentes Posts y ShowPost anidados:

<?php
namespace App\Livewire;
use Illuminate\Support\Facades\Auth;
use Livewire\Component;
class Posts extends Component
{
public $postLimit = 2;
public function render()
{
return view('livewire.posts', [
'posts' => Auth::user()->posts()
->limit($this->postLimit)->get(),
]);
}
}
<div>
Límite de Publicaciones: <input type="number" wire:model.live="postLimit">
@foreach ($posts as $post)
<livewire:show-post :$post :key="$post->id">
@endforeach
</div>
<?php
namespace App\Livewire;
use Illuminate\Support\Facades\Auth;
use Livewire\Component;
use App\Models\Post;
class ShowPost extends Component
{
public Post $post;
public function render()
{
return view('livewire.show-post');
}
}
<div>
<h1>{{ $post->title }}</h1>
<p>{{ $post->content }}</p>
<button wire:click="$refresh">Refrescar Publicación</button>
</div>

Este es el aspecto que podría tener el HTML de todo el árbol de componentes en la carga inicial de la página:

<div wire:id="123" wire:snapshot="...">
Límite de Publicaciones: <input type="number" wire:model.live="postLimit" />
<div wire:id="456" wire:snapshot="...">
<h1>Primera Publicación</h1>
<p>Contenido Publicación</p>
<button wire:click="$refresh">Refrescar Publicación</button>
</div>
<div wire:id="789" wire:snapshot="...">
<h1>Segunda Publicación</h1>
<p>Contenido Publicación</p>
<button wire:click="$refresh">Refrescar Publicación</button>
</div>
</div>

Observe que el componente padre contiene tanto su plantilla renderizada como las plantillas renderizadas de todos los componentes anidados dentro de él.

Debido a que cada componente es independiente, cada uno tiene sus propios IDs y snapshots (wire:id y wire:snapshot) incrustados en su HTML para que el núcleo JavaScript de Livewire los extraiga y rastree.

Consideremos algunos escenarios de actualización diferentes para ver las diferencias en cómo Livewire maneja los diferentes niveles de anidamiento.

Actualización de un componente hijo

Si pulsara el botón “Actualizar publicación” en uno de los componentes hijo show-post, esto es lo que se enviaría al servidor:

{
memo: { name: 'show-post', id: '456' },
state: { ... },
}

Este es el HTML que se enviaría de vuelta:

<div wire:id="456">
<h1>Primera Publicación</h1>
<p>Contenido Publicación</p>
<button wire:click="$refresh">Refrescar Publicación</button>
</div>

Lo importante aquí es tener en cuenta que cuando se activa una actualización en un componente hijo, sólo los datos de ese componente se envían al servidor, y sólo ese componente se vuelve a renderizar.

Ahora veamos el escenario menos intuitivo: actualizar un componente padre.

Actualizando el componente padre

Como recordatorio, aquí está la plantilla Blade del componente padre Posts:

<div>
Límite de Publicaciones: <input type="number" wire:model.live="postLimit">
@foreach ($posts as $post)
<livewire:show-post :$post :key="$post->id">
@endforeach
</div>

Si un usuario cambia el valor de “Post Limit” de 2 a 1, se activará únicamente una actualización en el padre.

He aquí un ejemplo de cómo podría ser la carga útil de la solicitud:

{
updates: { postLimit: 1 },
snapshot: {
memo: { name: 'posts', id: '123' },
state: { postLimit: 2, ... },
},
}

Como puede ver, sólo la instantánea del componente principal Posts es enviada al servidor.

Una pregunta importante que puedes estar haciéndote es: ¿qué sucede cuando el componente padre vuelve a renderizar y encuentra los componentes show-post hijos? ¿Cómo volverá a renderizar los componentes hijos si sus instantáneas no han sido incluidas en la carga de la petición?

La respuesta es: no se volverán a renderizar.

Cuando Livewire renderiza el componente Posts, renderizará los marcadores de posición para cualquier componente hijo que encuentre.

Aquí hay un ejemplo de lo que el HTML renderizado para el componente Posts podría ser después de la actualización anterior:

<div wire:id="123">
Límite de Publicaciones: <input type="number" wire:model.live="postLimit" />
<div wire:id="456"></div>
</div>

Como puede ver, sólo se ha renderizado un hijo porque postLimit se actualizó a 1. Sin embargo, también notará que en lugar del componente hijo completo, sólo hay un <div></div> vacío con el atributo wire:id correspondiente.

Cuando este HTML es recibido en el frontend, Livewire transformará el antiguo HTML para este componente en este nuevo HTML, pero saltará inteligentemente cualquier marcador de posición de componente hijo.

El efecto es que, después de la transformación, el contenido final del DOM del componente principal Posts será el siguiente:

<div wire:id="123">
Límite de Publicaciones: <input type="number" wire:model.live="postLimit">
<div wire:id="456">
<h1>Primera Publicación</h1>
<p>Contenido Publicación</p>
<button wire:click="$refresh">Refrescar Publicación</button>
</div>
</div>

Consecuencias para el rendimiento

La arquitectura en “isla” de Livewire puede tener consecuencias tanto positivas como negativas para su aplicación.

Una ventaja de esta arquitectura es que le permite aislar partes caras de su aplicación. Por ejemplo, puede poner en cuarentena una consulta de base de datos lenta en su propio componente independiente, y su sobrecarga de rendimiento no afectará al resto de la página.

Sin embargo, el mayor inconveniente de este enfoque es que debido a que los componentes están completamente separados, la comunicación/dependencias entre componentes se hace más difícil.

Por ejemplo, si tuvieras una propiedad pasada desde el componente principal Posts al componente anidado ShowPost, no sería “reactivo”. Debido a que cada componente es una isla, si una petición al componente padre cambiara el valor de la propiedad que se pasa a ShowPost, no se actualizaría dentro de ShowPost.

Livewire ha superado varios de estos obstáculos y proporciona APIs dedicadas para estos escenarios como: Propiedades Reactivas, Componentes Modelables y el objeto $parent.

Armado con este conocimiento de cómo funcionan los componentes anidados de Livewire, serás capaz de tomar decisiones más informadas sobre cuándo y cómo anidar componentes dentro de tu aplicación.