To work with Livewire, you must understand what is happening under the hood.
Form Refresh without Page Refresh
In the previous lesson, I added HTML and Tailwind header/footer code. Let me show you that it is NOT reloaded when we click "Submit".
If we open the browser Network tab, we see the POST request to the /livewire/update
URL.
And if we take a look at what it returns, the main thing to understand is the "html"
part.
If we zoom a bit further to the right, we see this:
So, Livewire request to the server returns the HTML of the component to re-render in the browser.
Remember the structure of our main Blade file?
resources/views/companies/create.blade.php:
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Simple Form</title> <script src="https://cdn.tailwindcss.com"></script></head><body class="flex items-center justify-center min-h-screen bg-gray-100"> <livewire:company-create /></body></html>
So yeah, Livewire doesn't return the full HTML of that page. It returns the HTML of only the part inside <livewire:company-create />
to re-render only the component of the form.
resources/views/livewire/company-create.blade.php
<div class="w-full max-w-md p-6 bg-white rounded-lg shadow-md"> <!-- Success Alert --> @if ($savedName != '') ... @endif <!-- Form --> <form wire:submit="save"> ... </form></div>
So, Livewire calls the render()
method of the component class and re-renders the underlying Livewire Blade but with all the updated values of the variables, which is then shown in the browser.
That's why, technically, it's the same single-page behavior as with React/Vue, refreshing only the part of the page we need.
However, a big difference is that there's a call to the server to achieve that. In comparison, JS frameworks may or may not perform such a server call. This is, in fact, one of the main criticisms of Livewire: too many server calls, especially if you have many dynamic components on the same page.
But Livewire compensates that with great developer experience:
- You mainly work with the same familiar Laravel + Blade syntax
- No need to run any
npm
commands or re-compile JS
wire:model and wire:model.live
So, we examined what is happening with wire:submit
. What about wire:model
? Do they also perform server requests on each change?
By default, no.
Binding the property with a simple wire:model
will send the value to the server only when needed, like with the Submit method.
However, if you put wire:model.live
, as we did for the Country dropdown, we do have a server call. And this is exactly what we need: refresh the form right away, right?
Notice: in the earlier Livewire 2 version, the default was the opposite (live
was the default), which caused performance criticism and Caleb switched the defaults in Livewire 3.
$this->reset(): Resets to WHAT?
Another example of what's important to understand in how Livewire works.
Remember we did this after the form was submitted?
app/Livewire/CompanyCreate.php:
public function save(): void{ // ... $this->reset('name', 'country', 'city', 'cities');}
This method $this->reset()
causes the listed properties to get back to their original default values and then re-renders the form again.
But there's a catch: what if the property doesn't have the original default value?
Look at how we initially define the variables:
app/Livewire/CompanyCreate.php:
class CompanyCreate extends Component{ public $countries; public $name; public $country; public $city; public $cities = []; public $savedName = ''; public function mount() { $this->countries = Country::all(); } // ...
- Some properties have no initial values (so, NULL by default)
- Other properties are assigned the static default values like
[]
or''
- Properties that need more logic are moved to the
mount()
method, which is a constructor or Livewire component because PHP OOP syntax wouldn't allow putting Eloquent operation as a default value
These are important distinctions.
So, if we perform $this->reset()
, does it get back to the value in the Class OOP or to the value in the mount()
?
For example, what happens if we do this:
$this->reset('countries');
Or, with another syntax, what if we just reset all properties?
$this->reset();
If you don't put any parameters, Livewire will reset all the public properties.
The answer is we would get an error after the form is submitted:
Livewire is trying to show the $countries
array, which is... null now?
The reason is because $this->reset('countries')
puts it back to the original value in OOP, which is non-existent:
app/Livewire/CompanyCreate.php:
class CompanyCreate extends Component{ public $countries;
So, the method mount()
is called only once when initializing the Livewire component.
In this situation, we should NOT reset the full component. In general, it's better to specify which properties to reset()
and not blindly reset all of them.
Also, defining the original values+types for all public properties may be beneficial to avoid unexpected bugs:
// ... use Illuminate\Support\Collection; class CompanyCreate extends Component{ public Collection $countries; public string $name = ''; public string $country = ''; public string $city = ''; public Collection $cities; public string $savedName = ''; public function mount() { $this->countries = Country::all(); $this->cities = collect([]); } public function save(): void { // ... $this->reset('name', 'country', 'city'); $this->cities = collect([]); }}
As you can see, we define the $cities
property value in the mount()
, but we also need to refresh it to the empty Collection after the submission because otherwise, it would be reset to the default static value, which is null.
This is the GitHub commit for this change.
This lesson was more theoretical, with only a few code changes. Still, it's extremely important to understand the Livewire lifecycle: when the Blade is re-rendered and with what property values.
With that knowledge, you can experiment and build your own forms with dynamic elements.
In the next lesson, we will discuss the form validation options.
No comments yet…