Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BLAZOR WASM] Lagging textarea binding with large amount of text #17951

Closed
michuny25 opened this issue Dec 18, 2019 · 13 comments
Closed

[BLAZOR WASM] Lagging textarea binding with large amount of text #17951

michuny25 opened this issue Dec 18, 2019 · 13 comments
Assignees
Labels
area-blazor Includes: Blazor, Razor Components question
Milestone

Comments

@michuny25
Copy link

michuny25 commented Dec 18, 2019

I am using textarea in Blazor webassembly (core 3.1) which is binded to a property in the page. I am using bind-value and bind-value:event because when I use only bind="Property" then It does not update as the user is typing which is my use case. I am using this binded value to send requests and save current content of the textarea after the user stops typing.

<textarea @bind-value="Note.Content" @bind-value:event="oninput" @onkeyup="@NoteTypingKeyUp"></textarea>

@code {
    [Parameter]
    public NoteDetailViewModel Note {get; set;}
    private Timer timer;
    protected override async Task OnInitializedAsync()
    {
        //Initialize timer
        timer = new Timer(300);
        timer.Elapsed += NoteStoppedTyping;
        timer.AutoReset = false;
    }

    private void NoteTypingKeyUp(KeyboardEventArgs e)
    {
        // remove previous one
        timer.Stop();

        // new timer
        timer.Start();
    }

    private void NoteStoppedTyping(Object source, ElapsedEventArgs e)
    {
        if (Note.Id == null)
        {
            if (!string.IsNullOrWhiteSpace(Note.Content))
            {
                InvokeAsync(async () =>
                {
                    await CreateNote();
                    await LoadNotes();
                });
            }
        }
        else
        {
            InvokeAsync(async () =>
            {
                await UpdateNote();
                await LoadNotes();
            });
        }
    }

It works fine when the text in the textarea is short. For large amounts it causes UI delays as the user types letters are appearing with a very significant delay.

ezgif com-video-to-gif

Is it a known bug and is there some workaround?
Thanks

@michuny25 michuny25 changed the title Lagging textarea binding with large amout of text [BLAZOR WA] Lagging textarea binding with large amout of text Dec 18, 2019
@mkArtakMSFT mkArtakMSFT added the area-blazor Includes: Blazor, Razor Components label Dec 18, 2019
@michuny25 michuny25 changed the title [BLAZOR WA] Lagging textarea binding with large amout of text [BLAZOR WA] Lagging textarea binding with large amount of text Dec 18, 2019
@mrpmorris
Copy link

Please show your code for LoadNotes

@michuny25
Copy link
Author

michuny25 commented Dec 20, 2019

Please show your code for LoadNotes

There you go.

private async Task LoadNotes()
    {
        Console.WriteLine("Loading list of notes, searchContent: " + searchContent);

        notes = await authHttpClient.AuthGetJsonAsync<List<NoteListViewModel>>($"/api/notes?searchContent={searchContent}");

        StateHasChanged();
    }
    public class AuthHttpClientHelper
    {
        private readonly ServerAuthenticationStateProvider _authProvider;
        private readonly HttpClient _httpClient;
        private readonly DeviceState _deviceState;

        private string deviceId;

        public AuthHttpClientHelper(ServerAuthenticationStateProvider authProvider, HttpClient httpClient, DeviceState deviceState)
        {
            _authProvider = authProvider;
            _httpClient = httpClient;
            _deviceState = deviceState;
        }

        public async Task<T> AuthGetJsonAsync<T>(string url)
        {
            var authState = await _authProvider.GetAuthenticationStateAsync();

            if (authState.User.Identity.IsAuthenticated)
            {
                return await _httpClient.GetJsonAsync<T>(url);
            }
            else
            {
                if (deviceId == null)
                    deviceId = await _deviceState.GetDeviceId();

                Console.WriteLine("DeviceId: " + deviceId);
                
                if (url.Contains("?"))
                    return await _httpClient.GetJsonAsync<T>($"{url}&deviceId={deviceId}");
                else
                    return await _httpClient.GetJsonAsync<T>($"{url}?deviceId={deviceId}");
            }
        }
    }

However, I am pretty convinced this doesn't cause the delay, beause it fires only when the user stops typing, but the lagging is while the user presses keystrokes. Moreover, I tried to commend out the content of the LoadNotes() and the lagging textarea for huge text still persisted.

@mrpmorris
Copy link

Can you create a new Blazor wasm app that reproduces this?

@michuny25
Copy link
Author

michuny25 commented Dec 22, 2019

I have created a sample repo showing this lagging behaviour in the textarea:
https://github.com/michuny25/TextareaBugReproBlazorWasm

Just paste huge amounts of texts in the textarea and you will see the lagging letters as you type.

image

For me, the lagging in the sample demo started around the length of 7535 characters. In my own project, it starts much sooner, somewhere around 1000-2000 characters.

@michuny25 michuny25 changed the title [BLAZOR WA] Lagging textarea binding with large amount of text [BLAZOR WASM] Lagging textarea binding with large amount of text Dec 22, 2019
@mrpmorris
Copy link

This is a more minimal reproduction of the problem.

@page "/"

<textarea @bind-value="Content" @bind-value:event="oninput" style="height: auto; min-height: 500px; width: 700px;"></textarea>

@code {
  public string Content { get; set; } = new string('x', 18000);
}

It seems the lag is when JS invokes Blazor in order to update Content

@michuny25
Copy link
Author

michuny25 commented Jan 3, 2020

Any update on fixing this?

Also, I noticed another issue with binding the value of a textarea to a property on 'oninput' event:
image
Every time the user presses a keystroke the whole component is re-rendered which for bigger components means the UI is extremely lagging as the user is typing and is unusable.

Is there some workaround for binding the value of a textarea (in my case representing note) to a property on 'oninput' event without having the whole component to be re-rendered?

@mrpmorris
Copy link

Is there some workaround for binding the value of a textarea (in my case representing note) to a property on 'oninput' event without having the whole component to be re-rendered?

private int ValueHashCode = 0;
protected override bool ShouldRender()
{
  int lastHashCode = ValueHashCode;
  ValueHashCode = YourValue.GetHashCode();
  return ValueHashCode != lastHashCode;
}

@captainsafia
Copy link
Member

It's expected that Blazor components will trigger a re-render when an event handler is invoked. The solution that @mrpmorris shared above should help with programmatically controlling when re-renders happen.

As for the original performance issue, I profiled this on Edge with the latest version of Blazor WASSM (the RC, at the moment) with a textbox containing 18,000 characters. Each handler takes around 100ms to execute, which is slightly noticeable but not super laggy.

image

Some follow-ups:

  • Can you profile the keypress events so we can get some numbers on performance?
  • Are you able to repro the same lag on in more recent versions of Blazor?

@captainsafia captainsafia added the Needs: Author Feedback The author of this issue needs to respond in order for us to continue investigating this issue. label May 11, 2020
@mkArtakMSFT mkArtakMSFT removed this from the 5.0.0-preview4 milestone May 15, 2020
@mkArtakMSFT mkArtakMSFT added this to the Discussions milestone May 15, 2020
@ghost ghost removed the Needs: Author Feedback The author of this issue needs to respond in order for us to continue investigating this issue. label May 15, 2020
@michuny25
Copy link
Author

michuny25 commented May 27, 2020

Each handler takes around 100ms to execute, which is slightly noticeable but not super laggy.

That is correct. However, you have to look at the context. Who types text in textarea in rate of 1 character/second? The delay obviously stacks, so when you type quickly you get delay of seconds before you see the word in the textarea.

Please use the multiple reproductions posted above to try the real UI experience in context.
(OR look at the video I posted in the first comment - it's real-time video of the UI experience, NOT A GIF)

@mrpmorris
Copy link

You could also havr the element change event go to solve javascript and control how often it calls blazor. Say, immediately, but not again for another 50 or 100ms, the typing experience might be better that way.

@silverx69
Copy link

silverx69 commented Jul 8, 2020

I am having this problem too, it's very very easy to reproduce, you should not need an example from the community to reproduce this behavior, it's mind numbingly easy. Just bind-value and bind-value:event="oninput" or attach any onkey* event handler on a textarea OR input type="text" and hold a key down or try to type quickly, the words do not appear until you pause typing and let go of keys. This is very confusing to the user because they think it's not working until they stop typing and the text suddenly appears.

The strange part about this, is that I have an input box on another page that has an 'oninput' event handler and does not suffer from the same input lagg even though the event handlers are virtually identical.

I have tried every single combination of onkeydown, onkeypress, onkeyup, oninput, onchange, etc.. There is no input lagg on the element unitl I attach a blazor binding/event-handler, and it seems to only happen on complex pages during the "onkeyup" phase.

The lowest amount of lagg I had in the elements were when using "onkeyup" as an event handler. If you hold a key down, there was no lagg, but if you tried typing random characters quickly then it would lagg. So my guess is there's something on the "KeyUp" handler internally that's slowing things down when attached to blazor events/binding. I'm assuming a re-rendering (however it should be noted that the new characters appear, meaning rendering is still happening even with the keydown and there is no lagg).

@silverx69
Copy link

I think I might have found a workaround. @captainsafia had mentioned something that gave me an idea.

It's expected that Blazor components will trigger a re-render when an event handler is invoked.

When an input control or textarea is INSIDE of a larger, more complex Blazor component, the events cause the parent component to re-render it's contents. This is laggy when the Blazor component is large, however!

If you make a NEW component, and ONLY put the input box in that component and then add that component in the same place as the original input box. It's renders MUCH MUCH MUCH faster, and I have virtually no lagg.

So this problem is definitely tied to how complex the input/textarea's parent component is, and you can side skirt this issue by placing the input in a much less complicated container component.

@ghost
Copy link

ghost commented Sep 6, 2020

Thank you for contacting us. Due to a lack of activity on this discussion issue we're closing it in an effort to keep our backlog clean. If you believe there is a concern related to the ASP.NET Core framework, which hasn't been addressed yet, please file a new issue.

This issue will be locked after 30 more days of inactivity. If you still wish to discuss this subject after then, please create a new issue!

@ghost ghost closed this as completed Sep 6, 2020
@ghost ghost locked as resolved and limited conversation to collaborators Oct 6, 2020
This issue was closed.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-blazor Includes: Blazor, Razor Components question
Projects
None yet
Development

No branches or pull requests

6 participants