tomekowal

tomekowal

The topic of optimistic UIs in LiveView came up already e.g. How do you achieve Optimistic UI in Phoenix with LiveView? by @pillaiindu
but I wanted to show my solution.

LiveView team claims that LV is not suitable for optimistic UIs but I feel that there is a conceptually easy fix for that I present in this repo: GitHub - tomekowal/optimistic_counter: An experiment with optimistic UIs in Phoenix LiveView · GitHub

I described my solution in the optimistic_counter/README.md at master · tomekowal/optimistic_counter · GitHub

Let me know what you think about it!

Showing Posts 1 to 10

Exadra37

Exadra37

One recommendation:

Try out whatever you are trying to do in a commuting trip, like by train, bus or even as passenger in a car and then see if Live View fits the use case, because it will not if used to every DOM change, it will be sluggish.

In my product I am using Live View but keeping it to places where business logic needs to be applied to, anything else is not the best fit for Live View. User interactions with the DOM are best left to be handled in the client side.

Qqwy

Qqwy

TypeCheck Core Team

I think it is a great solution. I think that (simplified or full) vector-clocks might provide for a good way to resolve ‘stale’ front-ends.

One problem this approach (currently) has of course, is that the ‘optimistic’ handler needs to be written in plain JavaScript. This might change once Lumen can be used easily, or maybe one could purpose ElixirScript for it. Then again, the parts of a UI that need to be optimistic are probably limited, so it might still be possible to do the bulk in Elixir-on-the-server anyway.

tomekowal

tomekowal OP

Yep, that is my concern too.
a) I need to write JS
b) it works only for state + event → new_state transformations (if there is something impure like DB call in handle_event then of course JS part can’t do it

Implementing the logic twice in two languages is error-prone. Maybe some kind of macro that brings the JS and Elixir implementations close to each other can at least make it easier to edit both at the same time.

On the other hand, multiplayer games are perfect examples of doing exactly that. The same logic is repeated in client and server. Server is the source of truth.

LostKobrakai

LostKobrakai

For such cases I’d currently rather look into node ssr for client and server side rendering of those components and try to couple it with a channels solution, which might work similar to liveview in the process spawning / callbacks, but skips all the complexity of the templating / template diffing.

tomekowal

tomekowal OP

Thanks for your input! If I understand you correctly, you propose to write the core event handling logic in JS so that you can reuse it both on the server and client and then use channels to synchronize the states.

It might work but LiveView gives me much more. The server LiveView process is a single source of truth and I loose that if I use plain channels. The state synchronization is very hard to do efficiently so I really appreciate how great LiveView is in reducing the payload size between client and server.

LiveView also gives me entire lifecycle in case of dropped connections. Doing all that using channels seems like a big hurdle that would end up in a half reimplementation of LiveView.

I might be greatly underestimating the task of adding event counters to client and server side of LiveView but after we have that, optimistic UI algorithms boils down to:

if server_clock >= client_clock do
  patch the dom as usual because server is always right
else
  don't patch the dom because user clicked/typed a bunch of things so there are still events coming from the server
end

It might need a separate “client modification timestamp” for each “gap” in the template and some JS functions to set that stamp when you modify those gaps on the client side. But it still seems like much less work than building custom solutions on bare channels.

LostKobrakai

LostKobrakai

You’ll lose that as soon as your client becomes stateful for the state you also deal with on the server.

This is exactly the reason, why liveview doesn’t synchronize state at all. There’s state on the server and there’s markup derived from this state. The markup is sent to the client and replaces existing markup. There’s the fancy markup diffing, where the client does indeed hold some amount of state (the static parts), but everything on the client is replaced in the case of a reconnect.

This basically boils down to an CRDT implementation. There’s e.g. automerge for JS, which implements crdts for json data and also has existing tooling for synchronising data via network connections. The “problem” for an elixir based solution is that you need node on the server or you need to re-implement the automerge crdt protocol in elixir. I’ve experimented with automerge from client to client proxied through phoenix channels in the past and it’s interesting tech, but also complex.

josevalim

josevalim

Creator of Elixir

FWIW, LiveView already does this internally. For example, imagine you have three buttons on the page. Once you click them, they change their text to “Processing…”. Processing each button takes 1 second. If you click all 3 of them one right after the other, the first response will come in t=1, then t=2, then t=3.

if LiveView did not track the events, once t=1 arrived, all buttons would revert to their original text, but it doesn’t.

So maybe we can generalize this a bit so people can also leverage this information in hooks or similar, but the whole infrastructure is already there. This may be possible, I think it is a great idea!

Not really. The goal with optimistic UI is to change the client as a way to optimistically guess what the server will return. There is no state is really. The changes you do on the client will be discarded as soon as the ack is received.

LostKobrakai

LostKobrakai

Seems like I misunderstood the intro. Seems like counters as examples nowadays automatically mean distributed state to me :sweat_smile:

tomekowal

tomekowal OP

Haha :smiley: I might not have been clear enough :slight_smile:

Thanks for validation! I’ll check if I am able to dig in into infrastructure enough to propose something meaningful.

I currently imagine the API like this:

  • tag parts of the template as “optimistic” to prevent updates with stale values from the server
  • allow hooking into JS LiveView events when they fire to do changes to those parts in the template (in the repo I am using phx-hook but I would like to tie optimistic updates to JS LV events instead of DOM lifecycle events)
  • some functions to modify the DOM but with setting the time of modification to current event time
josevalim

josevalim

Creator of Elixir

I have talked to Chris about an API like this:

  1. In a hook, you will be able to call this.syncPushEvent(...)

  2. this.syncPushEvent(...) will block all over-the-wire updates to the Hook, until an ack to the syncPushEvent is received. Custom JS code in the hook still runs though

  3. Once a reply for the push event is received, we call the hook updated callback, so you can apply the server state to the hook contents

It is basically the mechanism that we use for optimistic UIs in buttons and forms, except we are making it available for hooks too. WDYT?

Where Next? Top

Trending in Discussions Top

AstonJ
As the title says, please share what you’ve been up to with Elixir. Whether that’s been learning it, looking into it, making stuff with i...
2977 92995 915
New
AstonJ
The obligatory hello world thread! Who are you and where are you from? :stuck_out_tongue:
4616 55835 594
New
caslu
I want to open this thread for you all to discuss and help those who really like Ash but are still hesitant to use it in a real project. ...
New
arcanemachine
I was working on an Ecto migration and I needed a timestamp. So, for the nth time, I looked up the different data types for timestamps, a...
New
alexslade
Fly’s CEO posted this recently - Turn And Face The Strange · The Fly Blog It says that Fly is going all-in on sprites, which is a worry ...
New
Herve37
We’re evaluating API mocking tools for OpenAPI-based projects and would love to hear what other teams are using. We’re particularly inte...
New
matt-savvy
Is there a word for the ~> symbol used in Version strings? Do you also just call it a Squiggle Arrow™ ?!
New

Other Trending Topics Top

garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
mcass19
ExRatatui lets you cook up rich terminal UIs in Elixir, powered by Rust’s ratatui via Rustler NIFs. Build interactive terminal applicatio...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge & Solve. They are GUI (Emerge) and State management (S...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
wintermeyer
There are three potential reasons for members of this forum to have a look at https://vutuv.de You are tired or annoyed of LinkedIn. Yo...
New
aseigo
ICal is a library for interacting with iCalendar data. It parses iCalendars into typed Elixir structs via ICal.from_ics, and can prepare ...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews