LiveView updated hook not firing

For some reason I can’t get an updated hook to run.

I have a live view component that renders a list of divs from assigns like so

<%= render_many @items, MyAppWeb.ItemView, "_item.html", current_user: @current_user %>

The child template has some JS it needs to run when the div is added to initialize some functionality, so I added a hook to the div:

<div phx-hook="ItemMounter" id="item-card-<%= @item.id %>">
<% IO.inspect "render" %>
<!-- snip... -->
</div>

LiveViewHooks.ItemMounter = {
  mounted() {
     console.log("render!");
  },
  updated() {
    console.log("update!");
  }
}

When the component loads I see the first console log, but when I change the assigns and the dom is rerendered, I see the message in the phoenix log, but I never see the second console log. Am I missing something?

Did you update the LiveSocket object?

let liveSocket = new LiveSocket("/live", Socket, {
  hooks: LiveViewHooks,
  params: { _csrf_token: csrfToken },
});

Yes, the mounted callback is in the console log, so I know the hook registered correctly.

I’ve traced this issue to the attempt to initialize a Vue.js component inside the hook. Whether in the mounted or updated hook, it seems to work the first time, but then fails to initialize on the second time. In fact, no further js in the hook executes. So for example, the following causes the component to initialize correctly, but then disappear when the liveview updates, and the log in the update hook never runs. Instead there is an error TypeError: e is null

LiveViewHooks.ItemMounter = {
mounted() {
new Vue({el: this.el});
},
updated() {
console.log(“el:”, this.el);
}
}

I was hoping I would be able to mix and match liveview and vue.js components in my legacy app, but that was too good to be true it looks like. Not sure where the conflict is exactly but it seems to break hooks entirely.

Can you extract a minimal example and put it up somewhere?

Sample repo

When you load the app, you should see the Vue component mount and output the initial LiveView state “1”, but the log statement in the updated hook won’t run. Comment out the mount hook that inits the Vue and the log will work again.

I’m not seeing the error in console here, and it’s not the exact same setup, which would be hard to reproduce, but other than that it’s the same bug.

I guess once you initialize Vue over the element, Vue takes completely over this.el and LiveView loses control, thus why updated isn’t called (wild guess on my part);

In these situations with a JS library that likes control over its DOM, it’s best to not have LiveView fight with it and use phx-update="ignore". Sure, this is a trivial example, but I’ve found out it can take you pretty far.

In this case, adding and id to the element and an phx-update="ignore" made it work as it should:

- <div class="item" phx-hook="ItemMounter">
+ <div id="<%= item %>" class="item" phx-hook="ItemMounter" phx-update="ignore">

Peek 2020-05-07 20-57

Wow, thanks! I never would have thought to use the update ignore flag to support the expected function of the update hook.