How to speed up requests

I’ve made a very simple profile card with two views once is showing me the profile id,name and email and the second view just show the input fields for this three attributes.

This is working but it is “very” slow. When i’m hitting the button Edit on the show view, it takes more than 650ms(in best scenario, sometimes it takes more than 1.2sec) to load the edit view and vice versa.

How can I make this a little bit faster ?

Currently I’m using the a fresh Laravel 7 installation and livewire v1.0.
This is running on a homestead version 9.2.0.

Show View:

Profile Component:

namespace App\Http\Livewire\User;

use App\User;
use Illuminate\Validation\Rule;
use Livewire\Component;

class Profile extends Component
{


    public $user, $user_id, $name, $email;
    public $updateMode = false;

    public function mount(User $user)
    {
        $this->user = $user;
        $this->user_id = $user->id;
        $this->name = $user->name;
        $this->email = $user->email;
    }

    public function render()
    {
        return view('livewire.user.profile.resource');
    }

    public function edit()
    {
        $this->updateMode = true;
    }

    public function cancel()
    {
        $this->updateMode = false;
    }

    public function submit()
    {
        $attributes = $this->validate([
            'name' => 'required|min:6',
            'email' => ['required', 'email', Rule::unique('users')->ignore($this->user->id)],
        ]);


        $this->user->update($attributes);

        $this->updateMode = false;
    }

}

And this are the views:
resource.blade.php:

<div class="p-3">

  @includeWhen(!$updateMode,'livewire.user.profile.show')

  @includeWhen($updateMode,'livewire.user.profile.edit')

</div>

show.blade.php

<div>
    <div class="flex items-center py-2">
        <div class="w-1/4">
            <span class="text-gray-800">Id:</span>
        </div>
        <div class="w-3/4" >
            <span class="text-gray-700 font-semibold">{{ $user_id }}</span>
        </div>
    </div>
    <div class="flex items-center py-2">
        <div class="w-1/4">
            <span class="text-gray-800">Name:</span>
        </div>
        <div class="w-3/4" >
            <span class="text-gray-700 font-semibold">{{ $name }}</span>
        </div>
    </div>
    <div class="flex items-center py-2">
        <div class="w-1/4">
            <span class="text-gray-800">Email:</span>
        </div>
        <div class="w-3/4" >
            <span class="text-gray-700 font-semibold">{{ $email }}</span>
        </div>
    </div>
    <!-- Editing Buttons -->
    <div class="pt-3">
        <button type="button" wire:click.prevent="edit" class="py-1 px-2 rounded bg-blue-500 text-white font-semibold">Edit</button>
    </div>
</div>

edit.blade.php

<form wire:submit.prevent="submit">
    <div class="flex items-center py-2">
        <div class="w-1/4">
            <span class="text-gray-800">Id:</span>
        </div>
        <div class="w-3/4" >
            <span class="text-gray-700 font-semibold">{{ $user_id }}</span>
        </div>
    </div>
    <div class="flex items-center py-2">
        <div class="w-1/4">
            <span class="text-gray-800">Name:</span>
        </div>
        <div class="w-3/4" >
            <input type="text" class="w-1/2 border appearance-none py-1 px-2 rounded shadow focus:outline-none" wire:model="name">
        </div>
    </div>
    <div class="flex items-center py-2">
        <div class="w-1/4">
            <span class="text-gray-800">Email:</span>
        </div>
        <div class="w-3/4" >
            <input type="text" class="w-1/2 border appearance-none py-1 px-2 rounded shadow focus:outline-none" wire:model="email">
        </div>
    </div>
    <!-- Editing Buttons -->
    <div class="pt-3">
        <button type="submit" class="py-1 px-2 rounded bg-blue-500 text-white font-semibold" >Save</button>
        <a href="#" class="ml-2 text-red-500 font-semibold" wire:click.prevent="cancel">Cancel</a>
    </div>
</form>

I ran into the same issue, with the same implementation approach. I also noticed that it was a bit slow when Livewire would change the state to show/hide the form, so what I did was use Alpine.js for determining whether to show the form or not.

<div x-data="{ mode: 'view' }">
    <div x-show="mode === 'edit'">
        <div>
            <!-- display form here -->
        </div>

        <button wire:click="update">
            Save
        </button>
        <button @click.prevent="mode = 'view'">
            Cancel
        </button>
    </div>
    <div x-show="mode !== 'edit'">
        <div>
            <!-- profile displayed here -->
        </div>

        <button @click.prevent="mode = 'edit'">
            Edit
        </button>
    </div>
</div>

Doing it this way resolved the issue with displaying the form, which feels really snappy and quick. I have run into a different issue since that I’m not sure how to address.

If I begin to type in the form to update the name, and quickly hit the save button before the requests to update the component properties complete, then what ever the properties were at the time that I hit the save button are what gets saved to the database, creating a race condition.

I’m still relatively new to the Livewire paradigm so I don’t have a good answer for this yet.

1 Like

I’m new to Livewire, and the potential for race conditions caught my eye as well. The examples I’ve seen all show the form elements bound to the server via model attributes but I couldn’t see if there was a way of guaranteeing all fields were clean before form submission.

Off the top of my head there are two ways of tackling it. Either, disable the submit button while there are pending updates on the component.

Or, as you’re already using Alpine.js, submit the form data with your submit method and rehydrate your component properties in that method.

I actually found a fix to the race condition that I was running into. By using the lazy modifier, the property that I am updating will only get updated when the input loses focus, or the form is submitted. So now my form looks more like this:

<form @submit.prevent="submit">
    <input wire:model.lazy="something" id="something" name="something">

    <button type="submit">Submit</button>
</form>

Found out thanks to this comment.

3 Likes

I’ve since found an even better fix for this. I can’t take credit, I’ve had a long standing issue on github about my biggest gripe with livewire here. You can’t use wire:model.lazy="property" and wire:keydown.enter="method" on the same input because the method you call on keydown runs before the modeled property is hydrated.

The last comment on there came up with a brilliant workaround of using wire:keydown.enter="method($event.target.value)".

Looking at this from two use cases, let’s look at using it for a single input first, ie a search bar or something like adding a tag.

Originally I was doing something like this:

public $tagInput;

public function addTag(): void
{
    TagHandler::linkTagToModel($this->model, $this->tagInput);
    $this->tagInput = '';
}

<input wire:model="tagInput" wire:keydown.enter="addTag" />

Cant lazy it because of the call stacking incorrectly. Well using the idea from GertjanRoke in that issues page, if there’s no submit button and we only work on a keydown, we don’t even need that public property tracked in javascript or modeled to the input. We can just change it to:

public function addTag(string $tag): void
{
    TagHandler::linkTagToModel($this->model, $tag);
}

<input wire:keydown.enter="addTag($event.target.value)" />

If we want that submit button to also work as a fall back, we will still need it as a public property and have it modeled. This still isn’t ideal because there is going to be some extra work we have to do because even if we set $this->tagInput = '', after the method runs, the property is going to be rehydrated to what’s in the input box. The only way I see around this is to have an additional bool and check for it on the render to clear the property.

public $tagInput;
protected bool $clearTag = false;
public function addTag(string $tag): void
{
    $this->tagInput = $tag;
    TagHandler::linkTagToModel($this->model, $this->tag);
    $this->clearTag = true;
}

public function render()
{
    if ($this->clearTag) {
        $this->tagInput = '';
    }
    return view(...);
}

<input wire:model="tagInput" wire:keydown.enter="addTag($event.target.value)" />

It’s still not a proper solution, but it’s a good workaround for now and is another way to cut down on requests.

To go back to the original post

Alpine and smart modeling for this case is probably the best practice. There’s also the ongoing battle to optimize your mysql queries and database set up as much as possible. Livewire is basically fancy server side partials that takes care of ajax routes for you.

Unfortunately there’s only so much you can do software wise, and it comes down to hardware limitations. Doing the same exact action on the same page in three different areas, here’s the livewire request times:
Older laptop: 1500ms
Live dedicated server: 320ms
G7: 280ms (gaming laptop that is complete overkill for this example)

You can’t fully judge livewire speed on how your local is running. A slower initial page load is a lot less noticeable than a slow ajax request. Most people won’t notice a 3sec load time, but a 1.5sec ajax time will feel like forever. It’s one thing if all the rest of your livewire components are running smoothly and one is taking forever, but if they are consistent with each other you are on the right track.

You said you are using homestead which tells me you are running windows (you might see a small performance boost using laragon instead). Keep in mind that your computer is running a full blown gui os, with your resource hogging browser and ide running, plus everything else you have opened and what windows is doing in the background. When you deploy to a server it will be running a stripped down os with no gui to worry about, and only the services running required to host your site (should be anyway), and all of the resources it has available will be put to use for the purpose of serving your site.

Basically just a long way of saying that site speed in development all comes down to context.

Just to add to this. I’ve just refactored a big form component into Livewire and I was super excited about it, until I delayed to production.

On my local machine everything is instantaneous. On production (Heroku) it’s taking several seconds to execute anything.

Checkout this video.

+1 I also have this 1-2 sec lag. Pretty sure it’s my code, but I will be interested to know if this is a legit bug.

Regarding the race condition, this may be of use. On your submit button use the wire:dirty.class directive. This will bind whatever class you provide to your element. When the front-end data does not match the latest data from the backend the class will be added.

Example using Tailwind

<input wire:dirty.class="cursor-not-allowed" type="submit" />

1 Like