LiveView calls mount two times

connected?/1 is an official function, it is the official way to check if you are connected and use that to drive conditional logic.

3 Likes

As far as I understand it, this is not an “issue”.

The first rendering is useful to quickly render an usable (or not depending on your needs, as in my example where I just push a “page loading” text) page that notably will be visible by bots and before any JS in executed (SEO, etc…). On the second run, a server process is launched and kept alive for the duration of the liveview to track updates and push diffs to the client.

The full life-cycle is explained here :

1 Like

I don’t mean to imply “bug” by saying “issue” - the issue is, you need to know, somehow, whether you’re on the first run or the second run in order to do anything differently in mount. And my question is whether this is the intended way to do that - realizing that there may not be an “official” way. In my app (which I think should be a common case) the mount process is expensive, and I only want to do that once, although it works fine if I just naively let it run twice.

1 Like

If you’d like a walk-through of the process itself I heartily recommend this video, which is part of a liveview course: https://pragmaticstudio.com/tutorials/the-life-cycle-of-a-phoenix-liveview

1 Like

connected?/1 is indeed the way you’d defer expensive work on the dead render, at the cost of empty containers or loading states for crawlers and friends. In general, most folks will be fine ignoring the distinction b/w disconnected and connected mount, but doing this check is absolutely fine for the special case.

8 Likes

@chrismccord do you think there will ever be a way to pass content between the two phases so you don’t have to do a double db fetch? Or should we just stash in an ets cache ha

1 Like

If the connected?/1 idea is preferred, would it be easier to have
def mount(params, session, socket) and
def connected_mount(params, session, socket)

How about applications that do not care about SEO at all (like using LiveView for developing the admin interface of an app) and that do not need that first quick html rendering (because page is only really interactive when connected)?

It would be nice to have a way to add code only when connected without systematically checking for connected?/1 application-wide. Then one can just have those db calls in mount without having to ensure that those are not done twice through a check.

The static render is not primarily for seo. Proper seo is just so prominent given SPAs basically made it a problem in the first place.

Much more importantly: A browser cannot connect to a websocket connection immediately. It needs to do a plain http request first, which returns html linking some js, which then can attempt to connect to a websocket endpoint. So by definition there need to be two connection attempts to your server. On the http connection you don’t know yet if the client is even able to connect to the websocket endpoint for the second connection.

Given Phoenix cannot know exactly how you want to deal with that failure of the websocket connection failing it can just give an API to users, which allows them to implement it however they need. This is connected?/1. You can decide what you render in the static render vs. any connected renders – which is not just the second one, but any fresh websocket connection made (think about reconnects).

That’s the part about failure modes.

Now the other fact people often don’t think about is the case of everything going according to plan. I initially talked about the browser not being able to connect to websockets before having the necessary js. This only affects the very first request a user makes in a given session. After that the js is on the browser and navigation can happen completely over websockets (live redirects and patching), thereby completely skipping the static render. The new stuff is directly mounted in connected mode over websockets. This means you might not even need to be so concerned about db requests being made twice. Unless the page is commonly hit at the start of a user session it might not even be rendered statically all to often in the first place.

As for the code concern: If you over and over use the same code for doing one thing for the static render and some other custom thing only when connected nobody prevents you from abstracting that into some helpers, maybe even an additional behaviour with different callbacks + some macro for the boilerplate. There are means of DRYing this up, if you truely deal with each case the same.

10 Likes

I do similarly, but have taken it one step further: I put the “bare” mount/3 in a macro in a module, and use that from all my LiveView, providing a set of default bindings. It also does the checks for authentication, so I have both a connected and an authed mount and avoid duplicating that code all the time. The result is that in those LiveViews, from the codeit looks like it connects once wherever I can get away with that. …

3 Likes

No because you necessarily have data dependencies in the template for almost all cases cases of disconnected vs connected mount, so they need a shared code path.

Nope. This is part of the programming model. It’s very likely you don’t even need to consider it. If you are concerned, measure and optimize as needed :slight_smile:

1 Like

I just realized that part of the problem is you have no guarantee the ws connection hits the same erlang node as the http connection. You could probably finagle something with a token and ets as cache, but yeah, that should be a library on top of lv, not in lv itself, since not everyone needs something that heavy.

1 Like

Not only that. You don‘t even have a guarantee that a ws connection happens at all. And if it does, when it‘ll do so.

1 Like

Is it possible to have a fully static file to bootstrap the liveview? Assume I don’t care anything about SEO. Then mount() will only be called once.

Folks, you are over thinking this :slight_smile:

You have the option to defer loading until connected, cache, etc for expensive operations, which you’ll need to be carefully considering even if you weren’t using LiveView if you have such expensive ops. As with regular HTTP requests, make it work, then make it fast(er) as necessary. The disconnected + connected mount is also nuanced workload wise because while it may dup work on mount, it saves you from doing work on every other “request” as the user interacts with the application. You aren’t fetching current user, parsing and authenticating via HTTP headers, fetching permissions, etc just to begin processing an event.

4 Likes

Yes, this ^. Also, if you use live_redirect between pages, the double mount only happens once when the user hits the site, and then not on any other pages.

mount/3 is only as expensive as you make it.

5 Likes

But it is theoretically possible, right? If I want to make an analogy to JAMStack techs like Next.js, they can do pre-rendering and server-side rendering, however Phoenix right now can only do server-side rendering. Imagine if we can use Phoenix as a static site generator, with pages that can be further hydrated into live view? There must be some real world applications that can benefit from it.

If I want a static site generator, I’ll use a static site generator :slight_smile:

The current functionality works splendidly and is still a huge saver compared to for example hydrating auth on every single request on a page like you would do with GQL, web1.0 or SPA based flows.

That’s a whole different can of worms right there. Not only do you not want to do db queries for the static render, you don’t even want to need phoenix for it.

I am. However:

  • The various javascript based generators are very complex, slow, and if I break something it is very hard to debug (at least for me)
  • Hugo is fast. However its templates only offer limited functionality. If I want to do serious work to interact with a server side (Phoenix), I would need to write some Javascript, Go and Elixir at the same time?