Flash of Error on Initial Page Load with Phoenix LiveView - Where to Look?

Hey Elixir enthusiasts,

I’m working on a brand new Phoenix application and experiencing an odd issue with LiveView. When the page initially loads, I see a flash of an error that says something went wrong for less than a second, and then it’s gone. This happens even when the search_results are initially empty.

Here’s a snippet of my LiveView code:

defmodule MyAppWeb.HomeLive do
  use MyAppWeb, :live_view

  alias MyApp.Glossary

  def mount(_params, _session, socket) do
    menu = %{
      "Home" => "/",
      "Games" => "/games",
      "Apps" => "/apps"
    }

    if connected?(socket) do
      {:ok,
       assign(socket,
         menu: menu,
         search_query: "",
         random_vocabularies: Glossary.random_vocabularies(),
         # TODO: Implement if the user accessing directly to this page via querystring. ex: /w/:word
         search_results: []
       )}
    else
      {:ok, assign(socket, loading: true)}
    end
  end

  def render(%{loading: true} = assigns) do
    ~H"""
    MyApp is loading ...
    """
  end

  def render(assigns) do
    ~H"""
    <div class="p-4">
      <form phx-change="search" phx-submit="search" id="search-form">
        <div class="flex items-center mb-4">
          <input
            class="rounded-l-lg py-2 px-4 border border-gray-300 focus:outline-none focus:border-blue-500 flex-grow"
            type="text"
            name="search_query"
            value={@search_query}
          />
          <button
            class="bg-indigo-900 hover:bg-indigo-700 text-white py-2 px-4 rounded-r-lg"
            type="submit"
          >
            Search
          </button>
        </div>
      </form>

      <section>
        <h2 class="hidden"><%= @search_query %> related search search_results</h2>
        <div class="space-y-2">
          <%= for result <- @search_results do %>
            <div id={result.id} class="bg-gray-100 py-2 px-4 rounded-md hover:bg-gray-200">
              <h3 class="text-lg font-bold"><%= result.word %></h3>
              <p class="px-4 italic"><%= result.descp %></p>
            </div>
          <% end %>
        </div>
      </section>
    </div>
    """
  end

  def handle_event("search", %{"search_query" => query}, socket) do
    if String.length(query) > 1 do
      search_results = perform_search(query)
      {:noreply, assign(socket, search_query: query, search_results: search_results)}
    else
      {:noreply, assign(socket, search_query: query, search_results: [])}
    end
  end

  defp perform_search(query) do
    Glossary.find_vocabulary_by_word(query)
  end
end

And my deps:

  defp deps do
    [
      {:phoenix, "~> 1.7.7"},
      {:phoenix_ecto, "~> 4.4"},
      {:ecto_sql, "~> 3.10"},
      {:postgrex, ">= 0.0.0"},
      {:phoenix_html, "~> 3.3"},
      {:phoenix_live_reload, "~> 1.2", only: :dev},
      {:phoenix_live_view, "~> 0.19.0"},
      {:floki, ">= 0.30.0", only: :test},
      {:phoenix_live_dashboard, "~> 0.8.0"},
      {:esbuild, "~> 0.7", runtime: Mix.env() == :dev},
      {:tailwind, "~> 0.2.0", runtime: Mix.env() == :dev},
      {:swoosh, "~> 1.3"},
      {:finch, "~> 0.13"},
      {:telemetry_metrics, "~> 0.6"},
      {:telemetry_poller, "~> 1.0"},
      {:gettext, "~> 0.20"},
      {:jason, "~> 1.2"},
      {:plug_cowboy, "~> 2.5"},
      {:pow, "~> 1.0.20"},
      {:bamboo, "~> 1.5"},
      {:bamboo_smtp, "~> 2.1.0"},
      {:csv, "~> 2.3.1"}
    ]
  end

Sometimes, I even see a timeout in the web console when it’s making a query. But other than that, there’s no error or warning in both the web and app logs.

I haven’t modified the app or root layout files; they are as initially generated. The database schema, migrations, etc., are all set up correctly.

Has anyone else encountered a similar issue or have any suggestions on where I should look to troubleshoot this?

Thanks for your help!

live_flash_error

is this on initial visit, or only when you hard refresh? If it’s on refresh, then the flash showing the “we can’t find the internet” message is expected because the connection is being cut by the browser. If it’s on initial visit and there is not error in the logs then it’s something I need to take a look like, but it sounds like this is what you see on refresh.

Yes, it’s hard refresh :frowning: So, it’s okay? Is there a way to increase that time for checking, like if it takes a few seconds then show it.

Thank you Chris!

Hi again,

I was going to asking another related question but while writing here, I figured it out. So, I’m writing it for thanking live navigation feature of replace and for the documentation.

For a future reference I’d like to drop here my solution. Sometimes, the code snippet I see on this forum is very useful, so, maybe it will help someone else for LiveView navigation.

And of course any feedback is welcome, as there’s always a room for improvement.

defmodule MyAppWeb.HomeLive do
  use MyAppWeb, :live_view

  alias MyApp.Glossary

  def mount(params, _session, socket) do
    menu = %{
      "Home" => "/",
      "Games" => "/games",
      "Apps" => "/apps"
    }

    if connected?(socket) do
      word = params["word"]
      search_results = if word, do: perform_search(word), else: []

      {:ok,
       assign(socket,
         menu: menu,
         search_query: word,
         random_vocabularies: Glossary.random_vocabularies(),
         search_results: search_results
       )}
    else
      {:ok, assign(socket, loading: true)}
    end
  end

  def render(%{loading: true} = assigns) do
    ~H"""
    MyApp is loading ...
    """
  end

  def render(assigns) do
    ~H"""
    <div class="p-4">
      <form phx-change="search" phx-submit="search" id="search-form">
        <div class="flex items-center mb-4">
          <input
            class="rounded-l-lg py-2 px-4 border border-gray-300 focus:outline-none focus:border-blue-500 flex-grow"
            type="text"
            name="search_query"
            value={@search_query}
            phx-debounce="300"
          />
          <button
            class="bg-indigo-900 hover:bg-indigo-700 text-white py-2 px-4 rounded-r-lg"
            type="submit"
          >
            Search
          </button>
        </div>
      </form>

      <section class="my-8">
        <div class="text-sm space-x-4">
          <%= for {vocab, index} <- Enum.with_index(@random_vocabularies) do %>
            <.link
              id={vocab.id}
              title={vocab.word}
              lang={vocab.lang_code}
              patch={~p"/w/#{vocab.word}?src=random"}
              replace
              class={
                if rem(index, 2) == 0,
                  do: "text-gray-500 hover:text-gray-700",
                  else: "text-black hover:text-gray-700"
              }
            >
              <%= vocab.word %>
            </.link>
          <% end %>
        </div>
      </section>

      <section class="my-8">
        <h2 class="hidden"><%= @search_query %> related search</h2>
        <div class="space-y-2">
          <%= for vocab <- @search_results do %>
            <div id={vocab.id} class="bg-gray-100 py-2 px-4 rounded-md hover:bg-gray-200">
              <.link
                patch={~p"/w/#{vocab.word}?src=search"}
                title={vocab.word}
                lang={vocab.lang_code}
                replace
              >
                <h3 class="text-lg font-bold"><%= vocab.word %></h3>
                <p class="px-4 italic"><%= vocab.descp %></p>
              </.link>
            </div>
          <% end %>
        </div>
      </section>
    </div>
    """
  end

  def handle_event("search", %{"search_query" => query}, socket) do
    if String.length(query) > 1 do
      search_results = perform_search(query)
      {:noreply, assign(socket, search_query: query, search_results: search_results)}
    else
      {:noreply, assign(socket, search_query: query, search_results: [])}
    end
  end

  def handle_params(params, _uri, socket) do
    socket =
      case params["word"] do
        word when word != nil ->
          search_results = perform_search(word)
          assign(socket, search_query: word, search_results: search_results)

        _ ->
          assign(socket, search_query: nil, search_results: [])
      end

    {:noreply, socket}
  end

  defp perform_search(query) do
    Glossary.find_vocabulary_by_word(query)
  end
end