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

Keyboard based navigation is very very slow #93

Closed
boxxxie opened this issue Dec 25, 2024 · 7 comments
Closed

Keyboard based navigation is very very slow #93

boxxxie opened this issue Dec 25, 2024 · 7 comments

Comments

@boxxxie
Copy link

boxxxie commented Dec 25, 2024

LiveSelect and LiveView versions
using the demo site

Describe the bug
keyboard nav is slow to the point of looking like it doesn't exist

Expected behavior
navigation should be sub millisecond

Actual behavior
navigation takes a second or more

Browsers
Brave

@maxmarcon
Copy link
Owner

There's not much one can do here... LiveView is a server-side framework. Because every interaction requires a round-trip to the server, if the server is overloaded or the network latency is high the response to user's actions will be slow.

@boxxxie
Copy link
Author

boxxxie commented Dec 26, 2024

is it not possible to keep some client state? navigating a list that i can see taking seconds seems like a pretty serious UI issue. does liveview not have some way to do optimistic updates?

@boxxxie
Copy link
Author

boxxxie commented Dec 26, 2024

i'm a bit rusty with elixir, and haven't used liveview before. however i think there are ways to do this UI so that the user has a better experience.

one is to only do navigation on the client, but i think this would make it so if you wanted to sync the UI it wouldn't work.

another is to do nav on the client and server, but the client updates on it's own and the server passes events with some sync ID back to the clients, and the clients update based on the sync ID, however i think you can't avoid something like a CRDT if the component is to be used by n clients and be in some sane state for them all.

@maxmarcon
Copy link
Owner

maxmarcon commented Dec 26, 2024

is it not possible to keep some client state? navigating a list that i can see taking seconds seems like a pretty serious UI issue

First of all, it doesn't take seconds for everyone, certainly not for me:

Screen.Recording.2024-12-26.at.08.49.03.mov

The server for the showcase app is located in Frankfurt, and I'm in Berlin, so the latency looks reasonable to me. However, the further away you are, the worse it gets. I just tried to connect through a VPN from Australia, and the delay is indeed in the order of seconds. From New York it feels much better, still perhaps not optimal.

From your profile it appears that you're located in Toronto, so this might be what you're experiencing.

That said, what you're describing is certainly possible. One could keep the navigation state on the client and only send an update to the server when a user selects something. It would be complex and a bit tricky, but I believe you can do it.
However, if the problem is latency, then all your LiveView application will be affected, not just LiveSelect, so making LiveSelet more "client side" won't improve your general UX much, as the problem affects the whole application. Latency is one of the inherent drawbacks of using LiveView.

If UI updates are too slow then IMO you either have to: 1) reduce latency 2) use a client-side framework.

does LiveVIew not have some way to do optimistic updates?

I haven't used LiveView in a while and I might not be up-to-date with the most recent 1.0 development. However, I don't think LiveView has the tools to do something like this out-of-the-box. But, happy to be proven wrong of course: https://hexdocs.pm/phoenix_live_view/syncing-changes.html#content

@boxxxie
Copy link
Author

boxxxie commented Dec 26, 2024

i'm reading the link you provided.

yeah, i think you are correct that this type of optimization would have to be done for all frontend components. liveview doesn't seem to have this ability out of the box and code samples i was able to come up with involved CRDTs, pubsub, and a bit of JS. i don't think it would take much effort to add something like this to the existing system, however i would actually want this for all my components regardless of the framework.

it would be better if i could shove in my desired sync algorithm into your component, because depending on the use case it would be a bit overkill, like if it was only to be used by a single user. also it would set up how i would want my other components interfaces to exist.

@boxxxie
Copy link
Author

boxxxie commented Dec 26, 2024

reading through the link, and watching the video, i think using some or all of the below may be a solution

phx-keydown
phx-keyup
phx-window-keydown
phx-window-keyup

i see that the code is handling keydown events
https://github.com/maxmarcon/live_select/blob/913480c9222d21cfa7466568bf598d60ab4c3bbc/lib/live_select/component.ex#L256C3-L256C26
def handle_event("keydown", %{"key" => "ArrowDown"}, socket) do

but in the heex there isn't any JS functions being called. maybe these are new in liveview 1.0. so i think this may be as easy to fix as adding the phx- attribute and using JS.push("keydown") ... + edit classes on <li>
though, i don't exactly know where in the heex this would make sense or how i would debug the component to find out.

then i do a keydown this is the object sent to the server

[
    "4",
    "181",
    "lv:phx-GBSuvaPgZzLZ3h0R",
    "event",
    {
        "type": "hook",
        "event": "keydown",
        "value": {
            "key": "ArrowDown"
        },
        "cid": 1
    }
]

response

[
    "4",
    "181",
    "lv:phx-GBSuvaPgZzLZ3h0R",
    "phx_reply",
    {
        "status": "ok",
        "response": {
            "diff": {
                "c": {
                    "1": {
                        "11": {
                            "1": {
                                "p": {
                                    "0": [
                                        "\n",
                                        "\n"
                                    ]
                                },
                                "d": [
                                    [
                                        "cursor-pointer hover:bg-gray-400 rounded",
                                        "rounded px-4 py-1 text-white bg-gray-600",
                                        " data-idx=\"0\"",
                                        {
                                            "0": "Coimbatore",
                                            "s": 0
                                        }
                                    ],
                                    [
                                        "cursor-pointer hover:bg-gray-400 rounded",
                                        "rounded px-4 py-1",
                                        " data-idx=\"1\"",
                                        {
                                            "0": "Khabarovsk Vtoroy",
                                            "s": 0
                                        }
                                    ],
                                    [
                                        "cursor-pointer hover:bg-gray-400 rounded",
                                        "rounded px-4 py-1",
                                        " data-idx=\"2\"",
                                        {
                                            "0": "Pretoria",
                                            "s": 0
                                        }
                                    ],
                                    [
                                        "cursor-pointer hover:bg-gray-400 rounded",
                                        "rounded px-4 py-1",
                                        " data-idx=\"3\"",
                                        {
                                            "0": "Toronto",
                                            "s": 0
                                        }
                                    ],
                                    [
                                        "cursor-pointer hover:bg-gray-400 rounded",
                                        "rounded px-4 py-1",
                                        " data-idx=\"4\"",
                                        {
                                            "0": "Torreón",
                                            "s": 0
                                        }
                                    ],
                                    [
                                        "cursor-pointer hover:bg-gray-400 rounded",
                                        "rounded px-4 py-1",
                                        " data-idx=\"5\"",
                                        {
                                            "0": "Ulan Bator",
                                            "s": 0
                                        }
                                    ],
                                    [
                                        "cursor-pointer hover:bg-gray-400 rounded",
                                        "rounded px-4 py-1",
                                        " data-idx=\"6\"",
                                        {
                                            "0": "Victoria de Durango",
                                            "s": 0
                                        }
                                    ]
                                ]
                            }
                        }
                    }
                },
                "e": [
                    [
                        "active",
                        {
                            "id": "my_form_city_search_live_select_component",
                            "idx": 0
                        }
                    ]
                ]
            }
        }
    }
]

#my_form_city_search_live_select_component contains the and i think it contains the


    i guess this is where the phx- attribute would go and then any logic is from that element.

    it may be the case that this improvement is not so difficult to implement.

@maxmarcon
Copy link
Owner

maxmarcon commented Dec 26, 2024

but in the heex there isn't any JS functions being called

The event is generated in the JS hook: https://github.com/maxmarcon/live_select/blob/main/assets/js/live_select.js#L46

We're doing it this way so we can call event.preventDefault() to prevent an automatic form submission (at least this seems to be the reason as far as I can see, it's been a while).

Before that, it was using the phx- attributes. However, using phx attributes, a JS hook or a JS command won't make any deiffernce in terms of responsiveness: in all cases the event has to first go to the server, which handles it and sends back the response. This is the reason for the delay in the update.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants