phplaravel-livewire

How to update nested components without updating parent


I have a table with rows of job listing models. Within the table, it is possible to dispatch a bulk action onto the queue. In my parent component, I have an array $bulkIds and add an array of each dispatched bulk action. When the queue job is completed, I check whether the IDs for that job match the ones saved, and emit an event (bulkActionCompleted) if they match. This I receive on the nested components. I want to update the rows only within that array, but without updating the parent component. The user might be dispatching other queue jobs, or paginating to other results, etc. I don't want to interfere with the user.

I have tried working with skipRender, but you can't skip the rendering of a child, and as soon as the child updates, so does the parent.

Within the parent html:

<div id="job-listings-table" class="table-body col-12 p-0">
    @foreach($jobListings as $index => $jobListing)
        @php
            $isInBulk = in_array($jobListing->id, array_merge(...$bulkIds));
        @endphp

        <livewire:employer.job-listings.index.parts.row :jobListing="$jobListing"
                                                                    :isInBulk="$isInBulk"
                                                                    :lastJobIds="[]"
                                                                    :wire:key="$jobListing->id . now()">

       </livewire:employer.job-listings.index.parts.row>

                    
   @endforeach
</div>

Child component:

class Row extends Component
{
    public JobListing $jobListing;
    public bool $isInBulk;
    public array $lastJobIds = [];

    protected $listeners = [
        'bulkActionCompleted',
    ];

    public function render()
    {
        return view('livewire.employer.job-listings.index.parts.row');
    }

    public function bulkActionCompleted($jobListingIds)
    {
        $this->lastJobIds = $jobListingIds;
        if (in_array($this->jobListing->id, $jobListingIds)) {
            $this->isInBulk = false;
        }
    }
}

TL;DR: I want to update specific nested components without refreshing the parent.


Solution

  • As it turns out, this is default behaviour. Whenever I emit an event that gets caught by the child components, the parent doesn't get "fully" updated, when the function that emits the event uses skipRender. Adding wire:ignore to static items, fixed the issue that was caused when the function ran in the parent component.

    I can however not explain why parts of the parent still got updated. I guess skipRender doesn't quite skip the render of the whole component.

    To better explain, with skipRender called, the render function never even gets called, but if you have elements which you might have changed via JS (in my example, a dropdown that gets shown when at least one checkbox is selected), will be reset to its initial state. It seems to not re-render the DOM, but it does reset. By adding wire:ignore to these specific elements, the problem is resolved.