SyncedState

Online Documentation

SyncedState is a macro to help you sync state changes across Phoenix LiveViews. It's a wrapper around handle_event/3 and handle_info/2, and the reason for its creation is chronicled here

Usage

SyncedState is meant to be used in your liveview module. First, import SyncedState, and define a @topic and @endpoint module. Also, ensure that your liveview is subscribed to the topic:

defmodule MyAppWeb.WidgetLive.Index do
  use MyAppWeb, :live_view

  alias MyApp.Widgets
  alias MyApp.Widgets.Widget

  import SyncedState # import, not use

  @topic Atom.to_string(__MODULE__) # This will be used as the broadcast topic
  @endpoint MyAppWeb.Endpoint # This will be used to broadcast

  @impl true
  def mount(_params, _session, socket) do
    @endpoint.subscribe(@topic) # Make sure you're subscribed
    {:ok, stream(socket, :widgets, Widgets.list_widgets())}
  end

  ...

Next, use sync_state to define the event name you'd like to use. The sync_state macro expects a do block, and an after block:

defmodule MyAppWeb.WidgetLive.Index do

  ...

  sync_state "increment"
  # this block has the event payload in scope as `payload` and the socket in scope, and expects you to return a three element tuple of {handle_event_response, socket, result}
    %{"id" => id} = payload
    widget = Widgets.get_widget!(id)
    {:ok, updated_widget} = Live.Widgets.update_widget(widget, %{count: widget.count + 1})

    {:noreply, socket, updated_widget}
  after
  # this block has the result returned as the third element of the tuple above, and the socket in scope, and expects you to return a two element tuple of {handle_info_response, socket}
  {:noreply, stream_insert(socket, :widgets, result)}
  end

It's important to note: the first do block is executed in just the LiveView process which is handling that event, and the after block is executed in all LiveView process of the same type, so ideally the after block isn't doing anything but updating the socket.

Installation

If available in Hex, the package can be installed by adding synced_state to your list of dependencies in mix.exs:

def deps do
  [
    {:synced_state, "~> 0.0.2"}
  ]
end