LiveView re-mounted on long redirect

I have a live view that has one handler with something like this:

  @impl true
  def handle_event("login", params, socket) do
    # check login....
    if login_success do
        Process.send_after(
          self(),
          {:redirect_to, url},
          1000  # success_minimal_delay
        )

        {:noreply, assign(socket, display_success_message: true)}
    else
      {:noreply, assign(...)}
    end
  end

  @impl true
  def handle_info({:redirect_to, url}, socket) do
    {:noreply, redirect(socket, external: url)}
  end

The idea is to display the “login success” message for a bit, before redirecting.

Now, the “url” is an URL generated with a token in it that will log in the user into another system (SSO).

The issue is, this remote system can take up to several seconds to log in the user, but after like “100ms”, the live view is re-mounted.

So I have a flow like this:

  • handle_event("login")
  • success message is displayed
  • handle_info({:redirect_to...)
  • after like 100ms
  • live view is re-mounted
  • after the time the remote server took to answer the redirect (about 2000ms)
  • redirect actually takes place

I get that some timeout must be firing somewhere and re-mount the live view, but I don’t want that. I want the success message to be visible for success_minimal_delay + remote_server_response_time. This means I do not want the live view to be re-mounted, I’d like it to just wait for the redirect response.

Is there a way to prevent this behavior, or increase the timeout to something like 30sec?

What LiveView version? We disconnect the live socket before issues the external redirect, so the moment the client receives the redirect instructions it nukes the connection and does a window.location = .... With the disconnect, there should be no reconnect an remount so double check your LV version and report back. Thanks!

I made a small test case (using LV 0.16.4).

Also a video clearly illustrating the problem:

2 Likes

Is it possible that this is just a behaviour from the browser?

I noticed this crash as well yesterday. When I was trying to get scroll to top error from https://www.elixirconf.com appear. If go to that site and do following in the console liveSocket.enableDebug() and liveSocket.disconnect(). It seems that view crash is logged everytime disconnect() is called from the socket. So you can do liveSocket.connect() then liveSocket.connect() and everytime you get error like phx-FqfQiEKA0b-j4o4R error: view crashed - undefined in the console.

LiveView redirect is calling disconnect as you see here phoenix_live_view/live_socket.js at e7f0a4a5e0b6d76f10809d76840d5eb4e824259e · phoenixframework/phoenix_live_view · GitHub
But I’m not sure what is happening in your case because when calling .disconnect() I don’t see any reconnecting happening.

I think I pinpointed why this happens, but I am still not sure why it actually happens.

This is the way LiveView currently redirects

liveSocket.disconnect();
window.location = toUrl;

If you do it this way even from the console LiveSocket will try remount

liveSocket.disconnect(); window.location = "http://localhost:4000/slow";

But if you wait until LiveSocket is disconnected then redirect, it will work and LiveSocket doesn’t remount

liveSocket.disconnect(function () { window.location = "http://localhost:4000/slow" });

This needs to be patched into LiveView itself and I think you should create an issue for this at LiveView’s github page https://github.com/phoenixframework/phoenix_live_view

3 Likes

@wanton7 nice catch. I guess it might be how the JS engine works (“dying” on window.location = ).

@chrismccord If you agree this should be fixed with @wanton7 fix, I’ll open a PR.

I need to attempt to recreate myself. We don’t want to wait for disconnect acknowledgement because it will introduce RTT latency. My guess is the pagehide event is firing and causing us to reconnect, but it shouldn’t be once we disconnect. I will take a look. Thanks!

@kuon @wanton7 I can’t recreate on Phoenix 1.6. Can you try updating the demo app to phx 1.6 and report back? Thanks! Phx < 1.6 relied on different reconnect logic and I think that is what is causing the reconnects on page unload. Thanks!

2 Likes

Still does it with phoenix 1.6.

I updated my test app.

If this is a browser issue, I use firefox 92.0 on linux.

EDIT: tried with chromium, and I get the same issue.

As a sanity check, you blew away node_modules (or used the new esbuild) when you upgraded?

1 Like

I did cleanup node_modules, but then I realized that webpack would copy the compiled js into priv and it might be stalled, and yeah, it was (I hate webpack…).

I nuked the compiled files in priv and now I cannot reproduce the issue.

So I guess all is well. I am very sorry I didn’t catch that.

But this made me realize. It would be really nice to have a banner in the console if debug is enabled (or NODE_ENV or whatever). The banner would have the version as bare minimum, but things like link to the source is always a nice handy touch.

Like:

===================================
This is phoenix live socket client.
Build date: ...
Phoenix version: ....
Live view version: ....
Git commit: ....
Source: https://github.com/phoenixframework/phoenix_live_view/tree/e7f0a4a5e0b6d76f10809d76840d5eb4e824259e/assets/js/phoenix_live_view
Doc: https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html
===================================

This would just be console.log() when the library is loaded.

2 Likes