Readouble

Livewire v3 Hydration

Hydration

Using Livewire feels like attaching a server-side PHP class directly to a web browser. Things like calling server-side functions directly from button presses support this illusion. But in reality, it is just that: an illusion.

In the background, Livewire actually behaves much more like a standard web application. It renders static HTML to the browser, listens for browser events, then makes AJAX requests to invoke server-side code.

Because each AJAX request Livewire makes to the server is "stateless" (meaning there isn't a long running backend process keeping the state of a component alive), Livewire must re-create the last-known state of a component before making any updates.

It does this by taking "snapshots" of the PHP component after each server-side update so that the component can be re-created or resumed on the next request.

Throughout this documentation, we will refer to the process of taking the snapshot as "dehydration" and the process of re-creating a component from a snapshot as "hydration".

Dehydrating

When Livewire dehydrates a server-side component, it does two things:

  • Renders the component's template to HTML
  • Creates a JSON snapshot of the component

Rendering HTML

After a component is mounted or an update has been made, Livewire calls a component's render() method to convert the Blade template to raw HTML.

Take the following Counter component for example:

class Counter extends Component
{
    public $count = 1;

    public function increment()
    {
        $this->count++;
    }

    public function render()
    {
        return <<<'HTML'
        <div>
            Count: {{ $count }}

            <button wire:click="increment">+</button>
        </div>
        HTML;
    }
}

After each mount or update, Livewire would render the above Counter component to the following HTML:

<div>
    Count: 1

    <button wire:click="increment">+</button>
</div>

The snapshot

In order to re-create the Counter component on the server during the next request, a JSON snapshot is created, attempting to capture as much of the state of the component as possible:

{
    state: {
        count: 1,
    },

    memo: {
        name: 'counter',

        id: '1526456',
    },
}

Notice two different portions of the snapshot: memo, and state.

The memo portion is used to store the information needed to identify and re-create the component, while the state portion stores the values of all the component's public properties.

lightbulb Info: The above snapshot is a condensed version of an actual snapshot in Livewire. In live applications, the snapshot contains much more information, such as validation errors, a list of child components, locales, and much more. For a more detailed look at a snapshot object you may reference the snapshot schema documentation.

Embedding the snapshot in the HTML

When a component is first rendered, Livewire stores the snapshot as JSON inside an HTML attribute called wire:snapshot. This way, Livewire's JavaScript core can extract the JSON and turn it into a run-time object:

<div wire:id="..." wire:snapshot="{ state: {...}, memo: {...} }">
    Count: 1

    <button wire:click="increment">+</button>
</div>

Hydrating

When a component update is triggered, for example, the "+" button is pressed in the Counter component, a payload like the following is sent to the server:

{
    calls: [
        { method: 'increment', params: [] },
    ],

    snapshot: {
        state: {
            count: 1,
        },

        memo: {
            name: 'counter',

            id: '1526456',
        },
    }
}

Before Livewire can call the increment method, it must first create a new Counter instance and seed it with the snapshot's state.

Here is some PHP pseudo-code that achieves this result:

$state = request('snapshot.state');
$memo = request('snapshot.memo');

$instance = Livewire::new($memo['name'], $memo['id']);

foreach ($state as $property => $value) {
    $instance[$property] = $value;
}

If you follow the above script, you will see that after creating a Counter object, its public properties are set based on the state provided from the snapshot.

Advanced hydration

The above Counter example works well to demonstrate the concept of hydration; however, it only demonstrates how Livewire handles hydrating simple values like integers (1).

As you may know, Livewire supports many more sophisticated property types beyond integers.

Let's take a look at a slightly more complex example - a Todos component:

class Todos extends Component
{
    public $todos;

    public function mount() {
        $this->todos = collect([
            'first',
            'second',
            'third',
        ]);
    }
}

As you can see, we are setting the $todos property to a Laravel collection with three strings as its content.

JSON alone has no way of representing Laravel collections, so instead, Livewire has created its own pattern of associating metadata with pure data inside a snapshot.

Here is the snapshot's state object for this Todos component:

state: {
    todos: [
        [ 'first', 'second', 'third' ],
        { s: 'clctn', class: 'Illuminate\\Support\\Collection' },
    ],
},

This may be confusing to you if you were expecting something more straightforward like:

state: {
    todos: [ 'first', 'second', 'third' ],
},

However, if Livewire were hydrating a component based on this data, it would have no way of knowing it's a collection and not a plain array.

Therefore, Livewire supports an alternate state syntax in the form of a tuple (an array of two items):

todos: [
    [ 'first', 'second', 'third' ],
    { s: 'clctn', class: 'Illuminate\\Support\\Collection' },
],

When Livewire encounters a tuple when hydrating a component's state, it uses information stored in the second element of the tuple to more intelligently hydrate the state stored in the first.

To demonstrate more clearly, here is simplified code showing how Livewire might re-create a collection property based on the above snapshot:

[ $state, $metadata ] = request('snapshot.state.todos');

$collection = new $metadata['class']($state);

As you can see, Livewire uses the metadata associated with the state to derive the full collection class.

Deeply nested tuples

One distinct advantage of this approach is the ability to dehydrate and hydrate deeply nested properties.

For example, consider the above Todos example, except now with a Laravel Stringable instead of a plain string as the third item in the collection:

class Todos extends Component
{
    public $todos;

    public function mount() {
        $this->todos = collect([
            'first',
            'second',
            str('third'),
        ]);
    }
}

The dehydrated snapshot for this component's state would now look like this:

todos: [
    [
        'first',
        'second',
        [ 'third', { s: 'str' } ],
    ],
    { s: 'clctn', class: 'Illuminate\\Support\\Collection' },
],

As you can see, the third item in the collection has been dehydrated into a metadata tuple. The first element in the tuple being the plain string value, and the second being a flag denoting to Livewire that this string is a stringable.

Supporting custom property types

Internally, Livewire has hydration support for the most common PHP and Laravel types. However, if you wish to support un-supported types, you can do so using Synthesizers — Livewire's internal mechanism for hydrating/dehydrating non-primitive property types.

章選択

Packages

設定

バージョン変更
linkv3 linkv2
明暗テーマ
light_mode
dark_mode
brightness_auto システム設定に合わせる
テーマ選択
photo_size_select_actual デフォルト
photo_size_select_actual モノクローム(白黒)
photo_size_select_actual Solarized風
photo_size_select_actual GitHub風(青ベース)
photo_size_select_actual Viva(黄緑ベース)
photo_size_select_actual Happy(紫ベース)
photo_size_select_actual Mint(緑ベース)
コードハイライトテーマ選択

明暗テーマごとに、コードハイライトのテーマを指定できます。

テーマ配色確認
スクリーン表示幅
640px
80%
90%
100%

768px以上の幅があるときのドキュメント部分表示幅です。

インデント
無し
1rem
2rem
3rem
原文確認
原文を全行表示
原文を一行ずつ表示
使用しない

※ 段落末のEボタンへカーソルオンで原文をPopupします。

Diff表示形式
色分けのみで区別
行頭の±で区別
削除線と追記で区別

※ [tl!…]形式の挿入削除行の表示形式です。

テストコード表示
両コード表示
Pestのみ表示
PHPUnitのみ表示
OS表示
全OS表示
macOSのみ表示
windowsのみ表示
linuxのみ表示
JSフレームワーク
両フレームワーク
Reactのみ表示
Vueのみ表示
JSのみ表示

(JSが存在しない場合は、他を全表示)

和文変換

対象文字列と置換文字列を半角スペースで区切ってください。(最大5組各10文字まで)

本文フォント

総称名以外はCSSと同様に、"〜"でエスケープしてください。

コードフォント

総称名以外はCSSと同様に、"〜"でエスケープしてください。

保存内容リセット

localStrageに保存してある設定項目をすべて削除し、デフォルト状態へ戻します。

ヘッダー項目移動

キーボード操作