How to remove complexity of fat Phoenix LiveView


we have pretty old(autumn 2019) Phoenix LiveView module. I’m going to do refactoring of some logic inside. I will move some logic to own modules, contexts, etc. But it will be still fat module.

Current details:
LoC: 698
fn handle_* : 34
assign(*): 56

There is a lot of communication between frontend and backend. There is a lot of 3rd party JS libs and a lot of combinations. But it’s simple one page site where we generate some forms, js elements depends on current visitor configuration or data object setup.

So my question is: How can I remove this complexity of fat PhLV logic? We tried to create more LV with live_redirect but it’s not good idea for our use case. It’s question more about code readability than about domain/business logic. So I’m looking forward for some ideas or experience with this issue.



Have you thought about using LiveComponents? I wrote a short guide recently about it, might be useful so I’m sharing it here:

1 Like

thanks, I was thinking about it. I take about 1 hour yesterday and I figured out that is not good solution for me. Because when something is changed inside Component, whole component is re-rendered. And it’s not possible in my use case. Some “components” are stateful in frontend. For example 3rd party js sdk “iframe” is generated. So in case something is changed for this LV Component it would be re-render specific div. This div contain temporary stored data from JS SDK or iframe. So ti would be closed and I lost state.

It would be perfect to use components but I have to change html or re-render component only if I want to. Yes, I don’t have to give update to component when I don’t want. But it would be same issue what I have now. Huge parent LV and component would be just simple dump nested template.

Right… I’ll throw some more ideas, but obviously I don’t fully understand your use case and how things work:

  • you can use phx-update="ignore" on the div where the iframe renders, so LiveView won’t touch it when component re-renders the template;
  • copy over DOM elements like in this example for alpine.js; not sure how this will work with iframes.

Just in case you haven’t explored these.

1 Like

Yes, that’s true but I think phx-update=“ignore” will still re-render component on backend. Not sure.

But I already started with another approach.

I had something like this:

def handle_event("vendor1_some_event", %{ "somethign" => data}, socket) do
def handle_info({:vendor_1_another_event, some_data}, socket) do

So I had specific handlers for every event or message for vendors in LiveView module. This created that mess. I’m going to move all of these into 2 generic handlers. Something like this:

def handle_event( vendor_event, data, socket) do 
  assigns =  VendorResolver.handle_event(vendor_event, data, socket.assigns)
  {:noreply, %{socket | assigns: assigns}}

Similar for handle_info. VendorResolver will pattern match vendor and call function from vendor integration module. So I should have just 2 handlers after this kind of refactoring. Or I hope so. I will see how it will go. This should change how we add new vendor. Dev will have to do less changes. Just add new module with integration, add it to resolver and add template. At the end VendorResolver is not own module. It’s:

  • Vendors (Resolver)
  • Vendors.Vendor1
  • Vendors.VendorN

There is still some parts which I haven’t resolved. For example when I need send message to LiveView module from integration module. Process.send_after/4 should work but…

1 Like