In my Livewire component I have these methods (somewhat simplified for the purposes of the question):
class Form extends Component
{
protected mixed $page;
public function mount($pageId): void
{
// Get the page type here, which will determine the UI strings
$this->page = DB::table('pages')->find($pageId);
}
public function render(): View
{
return view(
'livewire.titlegen.form',
[
'page' => $this->page,
]
);
}
}
I render it from this Blade template:
<x-app-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
{{ __('Title') }}
</h2>
</x-slot>
<div class="py-12">
<livewire:titlegen.form :pageId="$pageId"/>
</div>
</x-app-layout>
As you can see, the pageId
in the template is passed to mount()
.
Now, the page initially renders fine; the component mounts, and then renders immediately. However if I call a Livewire method, then the component renders without mounting first. I get this error:
Typed property App\Livewire\Titlegen\Form::$page must not be accessed before initialization
I have tried adding $pageId
to the render method, in case that's a convention, but this does not work.
There is a detail in the documentation that shows this is expected behaviour:
In Livewire components, you use
mount()
instead of a class constructor__construct()
like you may be used to. NB:mount()
is only ever called when the component is first mounted and will not be called again even when the component is refreshed or rerendered.
[ Source ]
My solution was obvious in hindsight; the mount function should write its data to public, two-way binding parameters, which are then accessible to the render method even when mount is not called:
class Form extends Component
{
// Public properties are two-way bound
public int $pageId;
public function mount($pageId): void
{
$this->pageId = $pageId;
}
public function render(): View
{
$page = DB::table('pages')->find($this->pageId);
return view(
'livewire.titlegen.form',
[
'page' => $page,
]
);
}
}
In this example I've chosen to just pass the integer key, as I don't want this two-way binding channel to be passing large objects. That said, a database row object would probably be fine.