Back to Course |
Livewire 3 From Scratch: Practical Course

Parent-Child Components and Reactive Properties

In this lesson, let's see how Livewire's parent and child components can communicate with each other.


Initial Components

For this example, we will have two Livewire components. One for listing todos and the second for showing info about todos.

php artisan make:livewire TodosList
php artisan make:livewire TodoInfo

app/Livewire/TodosList.php:

use App\Models\Todo;
use Illuminate\Support\Collection;
use Illuminate\Contracts\View\View;
 
class TodosList extends Component
{
public Collection $todos;
public ?Todo $selected;
 
public function mount(): void
{
$this->todos = Todo::all();
 
$this->selected = $this->todos->first();
}
 
public function select(Todo $todo): void
{
$this->selected = $todo;
}
 
public function render(): View
{
return view('livewire.todos-list');
}
}

resources/views/livewire/todos-list.blade.php:

<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
<div class="p-6 text-gray-900 dark:text-gray-100 space-y-6">
 
@if($selected)
<livewire:todo-info :todo="$selected" />
@endif
 
@foreach($todos as $todo)
<div wire:click="select({{ $todo->id }})" @class(['bg-slate-100 px-3 py-2', 'border border-slate-800' => $todo == $selected])>
{{ $todo->title }}
</div>
@endforeach
</div>
</div>
</div>
</div>

app/Livewire/TodoInfo.php:

use App\Models\Todo;
use Livewire\Component;
use Illuminate\Contracts\View\View;
 
class TodoInfo extends Component
{
public Todo $todo;
 
public function render(): View
{
return view('livewire.todo-info');
}
}

resources/views/livewire/todo-info.blade.php:

<div>
<div><span class="text-gray-500">Title:</span> {{ $todo->title }}</div>
<div><span class="text-gray-500">Body:</span> {{ $todo->body }}</div>
<div><span class="text-gray-500">Due at:</span> {{ $todo->due_at }}</div>
</div>

After adding some data to the todos table, we will have a list of todos.

todos list


Making Property Reactive

Now we have a simple list of todos and the first one as selected. If we try to select another todo, it gets selected, but the information about it doesn't update.

This is because Livewire tries to send as minimal requests to the server as possible. In this case, we need to make a child component property reactive. We must add the #[Reactive] attribute to make it reactive.

app/Livewire/TodoInfo.php:

use Livewire\Attributes\Reactive;
 
class TodoInfo extends Component
{
#[Reactive]
public Todo $todo;
 
public function render(): View
{
return view('livewire.todo-info');
}
}

Now when we click on any of the todos, the information also gets updated.


Calling Parent Method from Child Component

Usually, to call a method from the child component, we would need to make an event, then dispatch it from the child and listen for it in the parent Livewire component. Now we can call a method in the parent using the $parent magic variable.

Let's add a button to deselect selected to do.

resources/views/livewire/todo-info.blade.php:

<div>
<div><span class="text-gray-500">Title:</span> {{ $todo->title }}</div>
<div><span class="text-gray-500">Body:</span> {{ $todo->body }}</div>
<div><span class="text-gray-500">Due at:</span> {{ $todo->due_at }}</div>
<button wire:click="$parent.deselect()" class="mt-2 px-4 py-2 bg-gray-800 rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-gray-700">
Deselect
</button>
</div>

deselect button

Look at the wire:click directive for this button. Here we use $parent to tell Livewire that the method is on the parent Livewire component and the method's name is deselect.

app/Livewire/TodosList.php:

class TodosList extends Component
{
// ...
 
public function deselect(): void
{
$this->selected = null;
}
 
// ...
}