JS hooks do not receive the expected events from their corresponding live components

Hello!

I am facing an issue with JS hooks when there are multiple live components on the same live view.

More specifically:

  • I have one live view that contains two live components
  • each component is connected to a JS hook
  • each JS hook subscribes to a ping event using handleEvent
  • on update/2 each live component sends a ping event to its hook using push_event

The problem is that both hooks receive one ping event that is sent from the second component only; the first component’s event never reaches the client.

I have set up a minimal example that demonstrates this problem in this repo.

Any insight on this behaviour? Am I doing something wrong?

@kkentzo stateful live components are required to have unique DOM ids, but yours do not have any DOM ID at all: live-component-push-event-issue/counter_component.ex at master · kkentzo/live-component-push-event-issue · GitHub

1 Like

@benwilson512 thanks for the reply!

I have added a unique DOM id to the component but the problem persists unfortunately…

[UPDATE] Just to mention that my workaround for the moment is to use nested live views instead of components. I think that it’d be nice/interesting to understand what’s going on in the case of components though…

Effectively I observe the same behaviour that sounds like a bug.

Don’t use live view in live view for that simple case, instead you can :

# in the livecomponent
  def update(%{id: id, counter: counter}, socket) do
    send(self(), {:ping, id})

    {:ok, socket |> assign(id: id, counter: counter)}
  end

The send/2 will send a message to parent live view. So simply handle it like the following code:

# in the live view
  def handle_info({:ping, id}, socket) do
    {:noreply, push_event(socket, "ping", %{id: id})}
  end
1 Like

This was a bug that has been fixed on master, but I also think there is some confusion here on how push_event works. Events are “global” for all hooks and can be pushed from any LiveView or component and read by any hook using handleEvent on the client. In this case, we have a diff producing two ping events . In your app what will be received is both hooks receive ping-a and ping-b events. It appears you are expecting pushed events to be isolated to their components, but that’s not the case. To get that kind of behavior you can namespace your events, ie:

|> push_event("ping-#{id}", ...)
this.handleEvent(`ping-${this.dataset.id}`, ...)

I just fixed the dup event keys being eaten, but your code needs to change to behave how you want. Make sense?

3 Likes

@Matsa59: Indeed this is a better workaround than a nested live view - thanks!

@chrismccord: Makes full sense - yes, I am aware of the behaviour you are describing, it’s just that my trivial example was meant to demonstrate the “eaten” events.

Good to know that it’s now been fixed - many thanks!

1 Like