LiveBulkAsync - A library to allow LV's async functionality in update_many function

LiveBulkAsync is a small library that extends LiveView’s async support to work with LiveComponent’s update_many function.

My main use case for it is to load columns data in a table asynchronously. Imagine that you have a table that contains a column which data takes time to load (maybe it is from an external API, or it is an expensive DB query).

Using LiveBulkAsync you can leverage LiveComponent’s update_many function to avoid N + 1 issues and still do the load without blocking the component.

The API tries to mimic the LV’s async API as much as possible and have the same guarantees.

Here’s the equivalent to start_async:

defmodule MyComponent do
  @moduledoc false

  use Phoenix.LiveComponent

  def update_many(assigns_and_sockets) do
    assigns_and_sockets
    |> Enum.map(fn {assigns, socket} ->
      socket |> assign(assigns) |> assign(loading?: true)
    end)
    |> start_many_async(:content, &load/0)
  end

  def handle_async(:content, {:ok, content}, socket) do
    {:noreply, assign(socket, loading?: false, content: content)}
  end

  def handle_async(:content, {:exit, reason}, socket) do
    {:noreply, socket}
  end

  def render(assigns) do
    ~H"""
    <div id={@id}>
      <div :if={@loading?}>loading...</div>
      <div :if={not @loading?}>{@content}</div>
    </div>
    """
  end

  defp load! do
    # Load something here
    ["content 1", "content 2"]
  end
end

And here’s the equivalent to assing_async:

defmodule MyComponent do
  @moduledoc false

  use Phoenix.LiveComponent

  def update_many(assigns_and_sockets) do
    assigns_and_sockets
    |> Enum.map(fn {assigns, socket} ->
      socket |> assign(assigns) |> assign(loading?: true)
    end)
    |> assign_many_async(:content, fn -> load!() end)
  end

  def render(assigns) do
    ~H"""
    <div id={@id}>
      <.async_result :let={content} assign={@content}>
      <:loading>Loading...</:loading>
      <:failed :let={reason}><pre>{inspect(reason)}</pre></:failed>
        <div>{content}</div>
      </.async_result>
    </div>
    """
  end

  defp load! do
    # Load something here
    {:ok, %{content: ["content 1", "content 2"]}}
  end
end

For more information you can check its repo or hex package.

4 Likes