2

I encountered a problem inside my demo phoenix application. The application consists of an api which listens for message jsons and creates an appropriate entry into the ecto data base.

defmodule ServerLive.Messages.Message do
  use Ecto.Schema

  schema "messages" do
    field :content, :string
    field :sender_id, :integer
    field :timestamp, :utc_datetime

    timestamps()
  end
end

There is also a live view which displays the entries inside the message table. My goal is to implement an update on the live view and prepend the created message inside the socket whenever a message is being successfully created. For that I have tried to use the PubSub module and introduced a subscription to the topic "messaging" when the live view is being mounted.

defmodule ServerLiveWeb.Demo do
  use ServerLiveWeb, :live_view

  alias ServerLive.Messages

  @topic "messaging"

  def mount(_params, _session, socket) do
    # mount all messages in reverse order
    socket = assign(socket, :messages, Enum.reverse(Messages.list_messages()))
    # subscribe to messaging
    ServerLiveWeb.Endpoint.subscribe(@topic)
    {:ok, socket}
  end

  ...

  def handle_event("insert", message, socket) do
    socket = update(socket, :messages, fn m -> message <> m end)
    {:noreply, socket}
  end
end

The broadcast is being exectued from the message api controller

defmodule ServerLiveWeb.MessageController do
  use ServerLiveWeb, :controller

  alias ServerLive.Messages

  @topic "messaging"

  def create(conn, %{"message" => message_params}) do
    with {:ok, %Message{} = message} <- Messages.create_message(message_params) do
      # broadcast message
      ServerLiveWeb.Endpoint.broadcast(@topic, "insert", message)
      |> put_status(:created)
      |> put_resp_header("location", Routes.message_path(conn, :show, message))
    end
  end

  ...

Now the issue is that every time a an api request creates a message and a broadcast is performed, the mount function gets called instead of the event handle for an insert. What could be the problem?

I have already investigated with the debugger and the call stack of a second mount call is:

  • ServerLiveWeb.Demo.mount/3
  • Phoenix.LiveView.Utils.maybe_call_mount!/3
  • Phoenix.LiveView.Channel.verified_mount/5
  • Phoenix.LiveView.Channel.handle_info/2
Adam Millerchip
  • 20,844
  • 5
  • 51
  • 74
markzmbl
  • 21
  • 1
  • It looks like you're redirecting the user after the create was performed, is that correct? If so, that's equivalent to a page refresh and therefore the mount will be called again. – randyr Jan 06 '22 at 19:16
  • 1
    I'm not sure if this is related to your issue, but [`mount` actually gets called twice](https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html#c:mount/3), so you might want to use [`connected?/1`](https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html#connected?/1) to only subscribe once the websocket is open. More explanations [here](https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html#module-life-cycle). – sabiwara Jan 06 '22 at 23:29

1 Answers1

0

The issue seems to be the double called of mount. One for static and second for when the socket is connected

def mount(_params, _session, socket) do
  ServerLiveWeb.Endpoint.subscribe(@topic)
  {:ok, socket}
end

If you put the subcribe witin an IF as follows:

if connected?(socket), do: ServerLiveWeb.Endpoint.subscribe(@topic)

It should work as @sabiwara said.

thelastinuit
  • 311
  • 1
  • 5
  • 9