Skip to content

Component in nested LiveView failes after re-render #3953

@kevinschweikert

Description

@kevinschweikert

Environment

  • Elixir version (elixir -v): 1.18-otp-28
  • Phoenix version (mix deps): 1.7.21
  • Phoenix LiveView version (mix deps): 1.1
  • Operating system: macOS
  • Browsers you attempted to reproduce this bug on (the more the merrier): Firefox, Safari, Chromium
  • Does the problem persist after removing "assets/node_modules" and trying again? Yes/no: Yes

Actual behavior

This error was introduced in LiveView 1.1

phoenix_live_view.js:232 no component for CID 1 {}

phoenix_live_view.js:3207 Uncaught (in promise) TypeError: Cannot set properties of undefined (setting 'newRender')
    at Rendered.recursiveCIDToString (phoenix_live_view.js:3207:27)
    at Rendered.dynamicToBuffer (phoenix_live_view.js:3190:47)
    at Rendered.toOutputBuffer (phoenix_live_view.js:3135:14)
    at Rendered.recursiveToString (phoenix_live_view.js:2921:12)
    at Rendered.toString (phoenix_live_view.js:2904:45)
    at phoenix_live_view.js:4675:57
    at LiveSocket.time (phoenix_live_view.js:5909:16)
    at _View.renderContainer (phoenix_live_view.js:4672:30)
    at phoenix_live_view.js:4661:40
    at LiveSocket.time (phoenix_live_view.js:5909:16)

Reproduction

  • Click "Show"
  • Click "Close"
  • Click "Show" again
  • See error message in browser inspector console

The introduction of <.live_component module={Component} id="comp2" /> in the code creates the issue

#!/usr/bin/env elixir
Mix.install([
  {:phoenix_playground, "~> 0.1.8"}
])

defmodule HomeLive do
  use Phoenix.LiveView
  alias Phoenix.LiveView.JS

  def mount(_params, _session, socket) do
    {:ok, socket}
  end

  def handle_params(_params, _url, socket) do
    socket = if socket.assigns.live_action == :show do
      socket |> assign(show: true)
    else
      socket |> assign(show: false)
    end

    {:noreply, socket}
  end

  def render(assigns) do
    ~H"""
    <.live_component module={Component} id="comp" />
    <button phx-click={JS.patch("/show")}>Show</button>
    <%= if @show  do %>
      {live_render(@socket, NestedViewLive, id: "nested_view", session: %{})}
    <% end %>
    """
  end
end

defmodule NestedViewLive do
  use Phoenix.LiveView

  def mount(_params, _session, socket) do
    {:ok, socket}
  end

  def render(assigns) do
    ~H"""
    Nested Content
    <.live_component module={Component} id="comp2" />
    <.link patch={"/"} > Close </.link>
    """
  end
end

defmodule Component do
  use Phoenix.LiveComponent

  def render(assigns) do
    ~H"""
    <div>
    Component
    </div>
    """
  end
end

defmodule DemoRouter do
  use Phoenix.Router
  import Phoenix.LiveView.Router

  pipeline :browser do
    plug :accepts, ["html"]
    plug :fetch_session
    plug :put_root_layout, html: {PhoenixPlayground.Layout, :root}
    plug :put_secure_browser_headers
  end

  scope "/" do
    pipe_through :browser

    live_session :test do
      live "/", HomeLive
      live "/show", HomeLive, :show
    end
  end
end

PhoenixPlayground.start(plug: DemoRouter, port: 4001)

Expected behavior

To use components in nested live views again

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions