skip to content

In Livewire 4, when a user edits the quantity of one row in a public $items array, which update hooks fire and with what arguments?

level: middleimportance: should knowfreq 32%

answer

  1. the path is dotted: items.2.quantity
  2. updated() gets the full path
  3. updatedItems($value, $key)
  4. key is everything after the first dot
  5. v4: whole-array replace fires once

basics

~10 s

For an update to items.2.quantity, Livewire 4 calls updating('items.2.quantity', $value) and updatingItems($value, '2.quantity') before the write, then updated('items.2.quantity', $value) and updatedItems($value, '2.quantity') after all updates are written.

solid answer

~40 s

Array properties are updated by **dotted path**, such as `items.2.quantity`. The generic `updating($path, $value)` and `updated($path, $value)` receive that full path. The property-level hooks `updatingItems($value, $key)` and `updatedItems($value, $key)` receive the new leaf value and the **key after the first dot**, here `'2.quantity'`; when the whole array is replaced, `$key` is `null` and `$value` is the full array. Livewire also calls a nested-name hook built from the path with dots turned into underscores and StudlyCased, whose `$key` is the segment after the **last** dot. In Livewire 4, replacing a whole array from the browser sends one consolidated update, so the hooks fire once with the new array instead of once per changed index as in v3.

code

php · 18 lines
php
<?php

use Livewire\Component;

new class extends Component {
    public array $items = [
        ['sku' => 'A-1', 'quantity' => 1, 'unitPrice' => 900],
        ['sku' => 'B-7', 'quantity' => 2, 'unitPrice' => 450],
    ];
    public int $total = 1800;

    // items.1.quantity -> $key === '1.quantity'
    public function updatedItems($value, ?string $key): void
    {
        $this->total = collect($this->items)
            ->sum(fn ($line) => (int) $line['quantity'] * $line['unitPrice']);
    }
};

go deeper

for a junior

Recall that array changes arrive as dotted paths and that updatedItems() receives the value and the key after the first dot.

for a middle

Explain the argument shapes of the generic and property-level hooks, the null key, and that updated hooks run after all writes.

for a senior

Treat the path as untrusted input and keep per-row recomputation targeted, since live inputs send many updates.

for a principal

Decide when a large editable table should stop living in one array property and move to child components or a form object.

## How Livewire addresses nested data A **public array property** such as `$items` on an order form can hold a list of lines, each with `sku`, `quantity` and `unitPrice`. The browser does not resend the whole array when one field changes; it sends an update for a **dotted path**, for example `items.2.quantity`. Livewire calls the `updating*` hooks, walks that path and writes the leaf value, and later calls the `updated*` hooks. Knowing what each hook receives is what lets one method recompute the right line. ## Which hooks fire for items.2.quantity | Hook | Called with | |---|---| | `updating($property, $value)` | `'items.2.quantity'`, new value | | `updatingItems($value, $key)` | new value, `'2.quantity'` | | `updated($property, $value)` | `'items.2.quantity'`, new value | | `updatedItems($value, $key)` | new value, `'2.quantity'` | So the property-level hook's `$key` is **everything after the first dot**. A typical order-form handler splits it: ```php public function updatedItems($value, ?string $key): void { if ($key === null) { $this->recalculateAll(); return; } [$row, $field] = explode('.', $key, 2) + [1 => null]; if ($field === 'quantity') { $this->recalculateLine((int) $row); } } ``` ## Nested-name hooks Livewire also tries a method built from the **whole path**: the dots become underscores and the result is StudlyCased, so a path `bar.baz` looks for `updatedBarBaz($value, $key)`. For that hook `$key` is the segment after the **last** dot. It is a niche feature; for list data with numeric indexes the property-level hook with its `$key` argument is the readable choice. ## When $key is null If the whole array is written at once, there is no sub-path, so the property-level hooks receive the complete new array as `$value` and `null` as `$key`. A handler must therefore treat `null` as "everything may have changed". ## What changed in Livewire 4 In **Livewire 3**, replacing a whole array from JavaScript (for example setting `$wire.items` to a new list) produced a series of granular updates, one per changed index plus special removal markers, so `updatedItems()` could fire several times for one user action. **Livewire 4** sends one consolidated update instead, so the hooks fire **once** with the full array. Editing a single row still sends a single-path update, so the per-row behaviour above is unchanged. Code that counted on per-index hook calls when a whole array is swapped needs to handle the `null`-key case. ## Timing inside the request - Each `updating*` hook fires as its own value is written, while the property still holds the old data. - All `updated*` hooks fire after **every** update in the request has been written, so a handler can read sibling fields that changed in the same request. - The action (if any) runs after that, then the view renders. ## Pitfalls interviewers look for 1. Assuming `$key` is the row index alone; it is the whole remaining path, `'2.quantity'`. 2. Forgetting the `null` key when the array is replaced wholesale. 3. Trusting the index: a user can send any path, so re-check that the row exists before using it. 4. Writing heavy per-row work in `updated()` without filtering on the path, which then runs for every property on the component. ## A readable pattern for list data For an order form with many lines, a clean structure is: 1. one `updatedItems($value, $key)` handler that splits `$key` into row and field; 2. a guard that the row exists and the field is one the user may edit; 3. a single `recalculateLine($row)` or `recalculateAll()` method that both the hook and any action call. This keeps the argument parsing in one place and makes the `null`-key case explicit. When the list grows large or each line gains its own behaviour, moving each line into its own child component gives every line its own simple, non-array update hooks; that decision belongs to how the component is structured rather than to the hooks themselves.

  • Why can an updatedItems() handler safely read another field that changed in the same request?
    Livewire writes every incoming update first and calls the `updated*` hooks only after all of them are applied. So a request that changes `items.0.quantity` and `discountCode` together lets the quantity hook see the new discount code.
  • Does validating inside updatedItems() protect the order total from a forged row index?
    Only if the handler checks it. The path comes from the client, so a request can target `items.99.quantity`. Guard the index and the value, or lock data the client must not change.

saying these in an interview costs you the question

  • Says the $key argument is only the numeric row index.
  • Expects updatedItems() to receive the full dotted path as its first argument.
  • Forgets that $key is null when the whole array is replaced.
  • Claims Livewire 4 still fires the hooks once per index on a whole-array replace.
  • Thinks updated hooks run before the other updates of the same request are written.