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

cblavier
Hey there, It’s been more than a year since we started using LiveView as our main UI library and building a whole library of UI componen...
New
wmnnd
Hey folks, I wanted to share with you a project I’ve been working on for a while: It’s an Open Source (AGPLv3) newsletter tool. Think Ma...
New
axelson
Hi there! :wave: @frigidcode and I (but mostly him) have been running an Elixir Book club, we’re almost done with Designing Elixir Syste...
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
sntran
In my third year of college, around 2010, I found Erlang. A few years later, I found Elixir. I liked the ideas at once: many small proces...
New
Ganna
Hi everyone, I have 8 years of production experience with Erlang/OTP and I’m just starting to get to know Elixir. What would you learn f...
New
gtcode
I’ve been tinkering with Lynx (by José) via hold_ledger. I used AI to write the code, a separate specification, the tests, and the Lean p...
New

Other Trending Topics Top

dominicletz
Hey there, I haven’t seen a list of useful Elixir specific skills for the popular AI tools yet so I’m starting one here with my first. ...
New
mudasobwa
I fully migrated to my own harness from Anthropic/Gemini and I think it’s time to share it. Welcome DSH, the DeepSeek Harness, fully writ...
New
KristerV
Hey. Is there anyone here who creates agents in their apps? Not talking about using agents, but creating them. I’m finding it pretty diff...
New
OndrejValenta
(I just needed to vent somewhere and LinkedIn is full of hope, or hype, I’m not sure which exactly) AI dream has many faces, but general...
New
webofbits
With AI doing more of the implementation work, I’ve been wondering how much coding I should deliberately keep doing myself. My main conc...
#ai
New
kip
In 2021 I started a new library called Tempo with the objective of modelling time as a set of intervals - not as instants. In 2022 I gave...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews