Why is handle_info not getting called in my LiveView app?

Hi. I’m creating a example phoenixliveview app and for some reason the handle_info callback doesn’t get triggered.

This is my

dummy_live.ex

What am I not seeing ?

defmodule MyApp.DummyLive do
  use Phoenix.LiveView
...
 def mount(_session, socket) do
    get_data(nil)
    {:ok, assign(socket, data: [])}
 end

...

 def get_data(search_term) do
    full_url = "#{@base_url}/items?item_type=live"
    headers = ["Client-ID": @client_id,
               "Accept": @api_version]
    {:ok, data} = get(full_url, headers)
    send(self(), {:data, data})
  end
...
  def handle_info({:data, data}, socket) do
      IO.inspect data
      {:noreply,  assign(socket, data: data)}
  end

end

Since LiveView is a process and the mount/2 callback is the code ran when the LiveView process starts up, you’ll want to subscribe to the same topic where your message {:data, data} is being broadcast.

LiveView runs the mount/2 twice, the second time when the web socket is connected. This means it’s a good idea to subscribe only when connected to the socket. You’ll see a lot of LiveView mount/2 code like this:

def mount(_session, socket) do
  if connected?(socket), do: SomePubSubSystem.subscribe("some_topic")
  get_data(nil)
  {:ok, assign(socket, data: [])}
end
2 Likes

I added this to the file:

def handle_info(_, socket) do
    IO.puts "handle_info"
    {:noreply, socket}
  end

Ran the app and executed this

iex> iex -S mix phx.server
iex> send(self(), [1,2,3])

And still, “handle_info” diesn’t get printed in the console. Shouln’t this worked regardless of subcription ?

I’m looking at this tutorial and they do something similar to what I’m doing here, so I think something else is amiss

send(self(), ...) will send the message to the iex process you’re using not necessarily the LiveView process where your handle_info callback is. Try using Process.info(self(), :messages) after you use the send command. It should show your message is in the iex mailbox.

It’s worth reading through the Phoenix PubSub docs if that’s what you’re using.

2 Likes

Put the send(self()…) In the mount function and you’ll see it fire. There is no magical way for your iex terminal’s process to know you mean to send a message to the completely separate liveview process.

Ok, so I refactored my code to use PubSub. Now my problem is that the socket is never getting connected:

  def mount(_session, socket) do
     IO.puts "mount"
    if connected?(socket) do
      IO.puts "Subscribing to topic" 
      DummyWeb.Endpoint.subscribe("twitch")
    end
    {:ok, init_data(socket) }
  end

For some reason the “mount” text is getting printed, but the “Subscribing to topic” isn’t. Am I missing something else ?

Why bother checking if it’s connected. Subscribe no matter what.

Found the problem. The JS API Changed recently:

It used be like this:

import LiveSocket from "phoenix_live_view"
let liveSocket = new LiveSocket("/live")
liveSocket.connect()

Now I changed it to this and it works:

import LiveSocket from "phoenix_live_view"
import {Socket} from "phoenix"

let liveSocket = new LiveSocket("/live", Socket)
liveSocket.connect()
1 Like

The server process renders the live view as the initial mount, and then the process exits. It doesn’t provide any benefit to subscribe at that point (it can’t do anything with the incoming message) and could potentially even cause errors if the web process is trying to handle a message that it doesn’t know about. So it’s a good idea to only subscribe when connected.

1 Like

Interesting. I’m curious as to what errors could crop up but thanks for the tip

I’d have to investigate further—it’s just a hunch now. If self() returns the pId of the web process, and the pubsub dispatches a message while that process is still alive, then the Web process would receive a message it doesn’t know about.

I haven’t tested this yet, which is why it may actually be safe to do.