如何在Phoenix LiveView的phx-update="stream"块中更新assigns
Phoenix LiveView Stream块内无法获取动态Assigns的问题解决
问题本质是Phoenix LiveView的Stream渲染优化机制:带有phx-update="stream"属性的容器,其内部模板仅在Stream数据(@streams.searched_notes)变更时才会重新渲染,普通Assigns(如@online_users_map)的更新不会触发该区域的模板重新计算,因此你在Stream块内的inspect始终显示初始的空值。
下面是两种可行的解决办法:
方法一:触发Stream条目重渲染
当online_users_map更新时,手动触发Stream条目重新渲染(无需重新加载数据,仅让LiveView重新渲染现有条目)。在处理Phoenix Presence更新的handle_info回调中,更新完online_users_map后调用stream/3:
def handle_info(%Presence.PresenceStateChanged{} = event, socket) do online_users_map = # 此处编写生成最新在线用户映射的逻辑 socket = assign(socket, :online_users_map, online_users_map) # reset: false 表示保留现有Stream数据,仅重新渲染条目 {:noreply, stream(socket, :searched_notes, socket.assigns.searched_notes, reset: false)} end
执行后LiveView会重新渲染所有Stream条目,此时就能获取到最新的@online_users_map。
方法二:用LiveComponent封装条目
将单个笔记条目封装为LiveComponent,把note和online_users_map作为props传入。LiveComponent会在props更新时自动重渲染,不受Stream容器的渲染限制:
- 创建LiveComponent:
# lib/your_app_web/live/note_component.ex defmodule YourAppWeb.NoteComponent do use YourAppWeb, :live_component def render(assigns) do ~H""" <li class="pb-2 sm:pb-2 mt-2 flex flex-row " id={@id}> <!-- 根据online_users_map判断作者在线状态 --> <%= if Map.has_key?(@online_users_map, @note.author_id) do %> <span class="text-green-500 ml-2">在线</span> <% end %> <!-- 渲染其他笔记内容 --> <div><%= @note.content %></div> </li> """ end end
- 修改原模板中的条目渲染:
<div> <%= inspect(@online_users_map) %> <ul phx-update="stream" phx-viewport-top={@page_no > 1 && "prev-page"} phx-viewport-bottom={!@end_of_timeline? && "next-page"} phx-page-loading id="searched_notess" class={[ if(@end_of_timeline?, do: "pb-4", else: "pb-[calc(200vh)]"), if(@page_no == 1, do: "pt-4", else: "pt-[calc(200vh)]") ]} > <%= for {dom_id, note} <- @streams.searched_notes do %> <%= live_component @socket, YourAppWeb.NoteComponent, id: dom_id, note: note, online_users_map: @online_users_map %> <% end %> </ul> </div>
这样每当@online_users_map更新时,所有NoteComponent都会接收新的props并重新渲染,正确展示作者在线状态。
内容的提问来源于stack exchange,提问作者Jason O.
相关产品推荐
相关产品推荐

