vincent15000's avatar

Private arrays with Livewire

Hello,

I initialize private arrays in the mount method and those arrays arent dynamic, they just content some datas that don't have to be shared to the front.

I have some input fields to type some values binded with wire:model.live.

Each time I type a value, the private arrays become empty.

What happens ?

Thanks for your help.

V

0 likes
14 replies
LaryAI's avatar
Level 58

When using Livewire, only public properties are preserved between requests. Private properties (including private arrays) are not persisted; they get re-initialized during every request, such as when you type into a wire:model.live bound input. This is why your private arrays become empty: Livewire doesn't store or serialize private/protected properties between updates.

What happens?

  • On each request, Livewire only serializes public properties.
  • Private arrays are not restored; they’re re-created in the mount method or via property initialization, often ending up empty if they depend on dynamic data.

How should you handle this?

  • If the data does NOT need to be sent to the frontend and should just be available for internal server logic, you can use private/protected properties, but their values must be set each request (for example, in mount and in every relevant method), not only once.
  • For persistent data, use a public property. You can make it protected/public and exclude it from frontend exposure using $except or casting.

Example solution:

class ExampleComponent extends \Livewire\Component
{
    public $inputValue;

    // Only for reference, NOT persistent between requests:
    private $myArray = [];

    public function mount()
    {
        // This array will not persist between requests
        $this->myArray = ['foo', 'bar'];
    }

    public function updated($propertyName)
    {
        // If you need $myArray on every request, always re-initialize here
        $this->myArray = ['foo', 'bar'];
    }

    // OR:
    // Prefer public if you need persistency, 
    // but then you'll have access from frontend via Livewire
    public $persistentArray = ['foo', 'bar'];
}

Summary:

  • Private/protected properties are NOT automatically persisted by Livewire.
  • If you need the array on the backend only, re-initialize it on every request, not just in mount.
  • If you want persistency, use a public property.

Further reading:
Livewire Lifecycle Hooks
Livewire Data Binding

Let me know if you need more clarification!

Snapey's avatar

As Lary says, data that is not exchanged with the frontend MUST be reinitialised on every request.

I usually do this in the render method.

2 likes
DoubleClickDesignLtd's avatar

@vincent15000, @snapey is right because it to do with how Livewire life-cycle works.

Let me step you through the logic so it clear for you, then I'll suggest 2 alternative ways of doing it rather than putting it in the render method.

In Livewire 3, when you type into an input with wire:model.live (or wire:model), Livewire performs AJAX requests on each keystroke. Each request:

  1. Sends the public properties to the server.
  2. Re-renders the component on the server.
  3. Sends back the updated DOM.

Private properties are not part of the payload because Livewire only syncs public properties with the frontend.

So, if you initialise your private arrays in mount() method, example like this:

private array $myArray;

public function mount()
{
    $this->myArray = ['a', 'b', 'c'];
}

Then as soon as Livewire sends a request from wire:model.live. Livewire creates a new instance of your component on the server and only hydrates public properties from the previous state. Private properties are not restored, so $myArray is now empty unless reinitialised

@snapey suggested reinitialising the private arrays in the render() method. This works because render() is called on every request, so your private arrays are reset each time.

Remember: ** In Livewire 3, private properties are never persisted across requests, so you always need to reinitialize them somewhere (render(), hydrate(), or make them public and control dehydration)**

Now you could do something very similar to @snapey using the hydrate method like this example

use Livewire\Component;

class MyComponent extends Component
{
    private array $myArray;

    // Initialize once on first mount
    public function mount()
    {
        $this->myArray = ['a', 'b', 'c'];
    }

    // Ensure private data exists on every Livewire request
    public function hydrate()
    {
        if (empty($this->myArray)) {
            $this->myArray = ['a', 'b', 'c'];
        }
    }

    public function render()
    {
        return view('livewire.my-component');
    }
}

The above works but I think it looks a bit messy.

What I normally do is use Livewire computed properties. No need for private + hydrate() trick and data stays server-only, computed each request. Here an example

use Livewire\Component;

class MyComponent extends Component
{
    public int $userId;

    #[Computed]
    public function myArray(): array
    {
        // Computed each render; never sent to frontend directly
        return ['a', 'b', 'c', 'user' => $this->userId];
    }

    public function render()
    {
        return view('livewire.my-component');
    }
}

Now if the Computed is a database query and you don't want to keep looking up the same information again and again, I normally use the cache facade something like this for example.

class MyComponent extends Component
{
    public int $userId = 1; // example public property

    #[Computed]
    public function user(): ?User
    {
        // Cache the result for 10 minutes
        return Cache::remember("user_{$this->userId}", now()->addMinutes(10), function () {
            return User::find($this->userId);
        });
    }

    public function render()
    {
        return view('livewire.my-component');
    }
}

Important

  • Computed methods cannot be private. They must be public to be recognized by Livewire.
  • They are read-only. You cannot bind wire:model to them.
  • Good for static arrays, derived data, server-only calculations, etc.

#[Computed] is cleaner replacement for private arrays used only on the server if your array doesn’t need to be modified by Livewire requests.

If you need to store state that changes and must survive requests, then you still need public properties + hydrate().

1 like
vincent15000's avatar

What I normally do is use Livewire computed properties. No need for private + hydrate() trick and data stays server-only, computed each request.

You are wrong ... computed properties are accessible to the frontend and this is specifically what I don't want, that's why I wanted private properties.

Snapey's avatar

you can call a public function from the front end, but you cannot access what it returns unless it is rendered or stored in a public property.

1 like
vincent15000's avatar

Oh you mean that a computed property can be used for other goal than return a value to be displayed in the frontend ?

DoubleClickDesignLtd's avatar

If you read the document it say it. Also you can test it yourself. I know it to be so but I understand why you are saying this.

1 like
Snapey's avatar

In Livewire 3, when you type into an input with wire:model.live (or wire:model), Livewire performs AJAX requests on each keystroke.

I wish you had not said that, and I am sure you know this is not necessarily the case, but this 'feature' is used by those who don't understand livewire as a negative. "watch your application collapse with all the requests". Yes, technically you could send every keystroke of every field - but why the hell would you?. You wouldn't do this with ajax, why do it with Livewire?

1 like
DoubleClickDesignLtd's avatar

@snapey just trying to explain the life-cycle as he wanted to know why his private values where empty.

Regarding this feature being used by others that don't understand it as a negative. Yes, you can create heavy system loads if you don't understand the life-cycle by sending far to many request but their are plenty of ways to work smarter and have large scale TALL stack application run smoothly.

1 like
vincent15000's avatar

It can be useful if you need a dynamic refresh while typing a value in a field (without having to click on a submit button). For example when you need to filter a list, just typing a search text and refreshing automatically the list. This way I usually use a debounce.

Please or to participate in this conversation.