Liveview local state handling in Phoenix 1.7 - is there a “usual” way for handling front-end state?

Hello everyone,

I have a question about handling front-end only state with liveview.

My example : I have 10 different buttons, and I want to keep track of the first three buttons clicked, and the order they were clicked.
I don’t want (and don’t need) to send every click to the server and keep this state in the assigns to rerender.

So I would like to handle this state front-end side only, and when the user clicks on “validate” => it sends the state to the server.

My question is : with now phoenix 1.7 and liveview 0.18, is there a “usual” way for handling front-end state? I can think of using alpine stores, or maybe an agent (I read this in a post but don’t know too much about them).

Also, I recently watched this [ElixirConf 2022 - Ryan Cooke - E2E Reactivity - using Svelte with Phoenix LiveView - YouTube] and it seems quite elegant => do you think this is maybe the best way to handle this (using a reactive variable)?

I’m mainly interested about if there is a tried and true way to handle this that I’m maybe not aware of. Or is there any goal about integrating front-side state management into Phoenix Liveview? (I’m just thinking about react with its multiple state management libraries where it can be really hard to choose, vs for example svelte where it is included and you don’t have to think about it).

Thanks by advance for your answers :slight_smile:

1 Like

This is not elegant (because it’s just aimed at a vanilla JS approach), but I think the answer lies somewhere in
https://hexdocs.pm/phoenix_live_view/js-interop.html#client-hooks-via-phx-hook
or
https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.JS.html#module-custom-js-events-with-js-dispatch-1-and-window-addeventlistener

Using JS event listeners / hooks you can then stay in client-space. I don’t think there’s a tried-and-true way to avoid the server aspect of LiveView, only because LiveView is all about the server being the ‘source of truth’

Thank you.

I forgot to say I don’t really want to use hooks for this because as you say it seems to complicated just to handle some state (I’d rather use an alpine store I think).

I was hoping there was a clean and elegant solution to this problem (because even if live view is server side I guess it is common to need to store a small state front side to then send it to the server? Maybe I’m wrong about this?)

LiveView is meant to be stateful to handle front-end state so there is no issue at all and the common way to achieve what you want is to send the events to the server.

I don’t think it is common to store “a little bit of state on the client”. Previously this was the case (i.e a modal open/closed state would be stored in the LV), but Hooks/JS changed this approach.

If your state matters it most likely belongs on the LV.

Ok thanks for your answer.

I think I will send it to the server then and do the usual way for my case.

But are you saying that there is no use case for storing state in the front? Like what tab is clicked, is a drop-down open or closed, things like that?

Did you mean to say: “was stored in the client” instead of “stored in the LV”?

I’m not sure I understood what you meant with: “but hooks/Js changed this approach”

I’m sorry about my wording.

This kind of state (modal/dropdown/tab etc) that is used to “show/hide” elements on the template was usually also stored in the LiveView, but nowadays there’s more elegant approaches to it where you don’t need to handle it on the LiveView (with Hooks/JS ← click this link, it’s not JavaScript that I meant).

Oh ok no problem. Then we agree. It is kind of my question : is there a ‘best’ way to keep front only state (let’s use the drop-down or tab example instead of my buttons).

So there is : maintaining state using hooks, using an alpine store (but maybe it needs to use the hooks also?).

Is there any other way? How does everyone store this state?

I’m okay with using hooks if it is the best way.

What you use will depend on what you’re trying to do. Hiding/showing stuff is very easy now owth LV.JS, more complex interactions could require a Hook, Alpine is also still valid, whichever fits you best.

This is an interesting article that is related to your question and for sure will provide more insights: Plucking the 'A' from PETAL · Fly

Thanks a lot for your help I’ll take a look at all of this.

  • for ‘easy’ state I see now I can use things like JS.hide or JS.show etc…

  • And for ‘more complex’ state (let’s say I want to keep in my state an array of objects for whatever reason), I can use hooks or an alpine store to handle this.

In my hook, on mount, I can add for example a ‘buttonClicked’ function, use a onclick=‘buttonClicked(Id)’ on my button with my hook, and then modify my state in the ‘buttonClicked’ function, and finally push the new state to the live view with `pushEventTo()'.

Is it the right ‘flow’ to do this? I’m on mobile but will try tomorrow.

I would typically create a live component to handle those events and store that state on the server side. There are reasons to move the state to the front end, but the small information here seems like it would be overkill.

I have found that storing state on the front and back end gets confusing and complex to understand when returning to the same code. It is also much simpler to end up in a position where the back and front ends don’t align with what they believe the data should be. Then you have to deal with conflict resolution.

Are the events going back and forth to the server, causing bottlenecks?

When I need a frontend state, I store it in simple js code. I will reach for the local storage APIs if I need long-term data storage across multiple sessions.