weakwire
LiveSub - PubSub-like functionality for LiveComponents without dealing with the LiveView
Hello people,
Big fan of elixir & phoenix LiveView.
While designing LiveView applications I use LiveComponent extensively.
Issue-Inspiration
I like LiveComponent to manage their state, but even with stateful components it is hard to abstract them away from the LiveView.
Usually there is a dependency to the LiveView (along with boilerplate code) when you want to talk to a different LiveComponent in the page, initialize the state of the component or make an async call.
That makes it harder to reuse the LiveComponent, test it, and the complexity of the LiveView increases by adding more LiveComponents.
Proposed solution
LiveSub https://gist.github.com/weakwire/d28dc8e5f9aa1edef78017ee308e7022 introduces a way for LiveComponents to talk to each other via a pub/sub like mechanism for the LiveView
Here is a LiveView application example, using LiveSub. https://github.com/weakwire/live_sub_example.
How to Use
- Use LiveSub in our LiveView and add your components with the
init=trueattribute
defmodule LiveSubExampleWeb.MyLiveview do
use LiveSubExampleWeb, :live_view
use LiveSub.LiveView
...
~H"""
...
<.live_component module={PeopleComponent} id={PeopleComponent} init={true} />
...
"""
...
- Define the “topics” that your LiveComponents are subscribed_to and/or the topics your LiveComponent emits
defmodule LiveSubExampleWeb.PeopleComponent do
@moduledoc false
use LiveSubExampleWeb, :live_component
use LiveSub.LiveComponent,
subscribe_to: [
"person_added",
"people_loaded"
],
emits: [
"people_loaded"
]
- When you define a topic in “emits”, a helper function is created and you can publish your message like so:
SubHelper.pub_person_added(%{name: "John", surname: "Smith"})
- To listen to a message, a local function is called inside the LiveComponent, generated using the topic name from the
subscribe_toparameter
defmodule LiveSubExampleWeb.PeopleComponent do
....
#This will be called when `SubHelper.pub_person_added` is called from a component
def sub_person_added(person, socket) do
socket |> assign(:person, person)
end
Result
- Adding N LiveComponents to a LiveView doesn’t increase the complexity of the LiveView
- Each LiveComponent is isolated and testable on it’s own.
- LiveComponents can be reused with minimal effort. For eg. an “Edit Person” component can by used in multiple LiveViews that show a person with 0 changes.
- The LiveComponent can initiate it’s state, even if it requires an async call
- Less boilerplate & complexity, inspires people to use more LiveComponents in their LiveViews
Drawbacks
- Currently you need to initialize the LiveComponent using init=true param. (For LiveSub to work, it requires the component id that unfortunately is not in the
mountcallback) -
LiveSubhijacks thedef update(%{id: id, init: true})of the LiveComponent. Unfortunately I don’t have any other ways to make it work. You can choose not to override it and you can manually initiate LiveSub.
Most Liked
ouven
Hi,
many thanks for your library!
I was thinking about your “Drawback 2” with the hijacking of the update function and a pattern popped into my mind, that I have seen some time ago. You can generate an overridable update function and call super in it. So you’re users can still have their own update function:
defmacro __using__(opts \\ []) do
quote do
@before_compile unquote(__MODULE__)
... more code
end
end
defmacro __before_compile__(%Macro.Env{} = env) do
[quoted_update(env)]
end
defp quoted_update(env) do
if Module.defines?(env.module, {:update, 2}) do
quote do
defoverridable update: 2
def update(assigns, socket) do
.... <your code>
super(assigns, socket)
end
end
else
quote do
@impl Phoenix.LiveComponent
def update(assigns, socket) do
.... <your code>
end
end
end
end
I hope it severs you ![]()







