LiveView updated hook fires every time instead of once

Dear Phoenix users,

I have a Phoenix umbrella project which uses LiveView (Phoenix 1.4.15 and LiveView 0.8.1). It contains a live view which has regular assigns that are updated approximately every 5 seconds and a graph (dygraphs JS) whose data is loaded with the press of a button (and might be updated at later time but is not for the moment).

The problem is that, once the button has been pressed and the data loaded, every time the other assigns are updated, the “updated” hook for the graph is called even if the graph data has not changed.

I made a minimal working example to check the problem without the rest of the code base and it is still present. The frequency of refresh has been set to 1 second. You can see on the screenshot that the Google JS console indicates that the updated message has been printed a total of 36 times instead of just one time, just after the press of the “update” button.

The question is : why does the update of @x (and not @values) leads to a call of the updated hook ?

The problem looks like LiveView updated hook problem but the versions of the software are different. Maybe I should have posted this question as a reply to the previous post even if it doesn’t answer the OP question.


The relevant edited code is included below.


let graph = null;
let Hooks = {};
Hooks.chart = {
  mounted() {
  updated() {
    const data = JSON.parse(this.el.dataset.values);
    if (graph === null) {
      const div = this.el;
      graph = new Dygraph(div, data, {
        labels: ["x", "y"],
        legend: 'always',
        connectSeparatedPoints: true,
        labelsDiv: "legend",
    } else {
      graph.updateOptions({ 'file': data });
let liveSocket = new LiveSocket("/live", Socket, { hooks: Hooks, params: { _csrf_token: csrfToken } });

Module PhxLv1Web.TestLive (file test_live.ex)

def render(assigns) do
  Phoenix.View.render(PhxLv1Web.TestView, "test.html", assigns)

def mount(_params, _session, socket) do
  if connected?(socket) do
    Process.send_after(self(), :update, 1000)
  {:ok, assign(socket, x: 0, values: nil)}

def handle_event("update", _value, socket) do
  IO.puts "update"
  values = (1..100) |> i -> [i, :rand.uniform(100)] end)
  {:noreply, assign(socket, :values, values)}

def handle_info(:update, %{assigns: %{x: x}} = socket) do
  Process.send_after(self(), :update, 1000)
  {:noreply, assign(socket, :x, x+1)}

Template test.html.leex

<p>x = <%= @x %>.</p>
<button phx-click="update">update</button>
<div id="legend"></div>
<div id="graph" phx-update="ignore" phx-hook="chart" data-values="<%= Jason.encode!(@values) %>"></div>

Try master. We are constantly providing enhanceents and bug fixes and this should have been solved. Make sure to remove assets/node_modules after upgrading. If the behaviour persists, please file a bug report.


From 0.8.0, Liveview added css loading classes that are applied phx-bound elements. See Loading state and errors.

EDIT 1: I’ve tested this on master, but it still has the same behaviour.

EDIT 2: If you comment out the logic inside updated, it won’t get called again; somehow, the Dygraph JS library creates this loop; indeed, this was created by the JS library itself; by default, it was redrawing itself every so often => thus the DOM was updated => updated hook was called correctly

{boolean} block_redraw
    Usually the chart is redrawn after every call to updateOptions(). If you know better, you can pass true to explicitly block the redraw. This can be useful for chaining updateOptions() calls, avoiding the occasional infinite loop and preventing redraws when it's not necessary (e.g. when updating a callback).

This has been fixed on master. For phx-ignore’d elements, we were invoking the updated hooks because as you figured out we detected the DOM change, but this should only happen if we detect a change in the data attributes on the hook element, so on master we now only invoke if the dataset has changed for an ignored element. Thanks!


Thanks for the suggestions. I will test it with master. And also try to understand the answer by @sfusato suggesting that dygraphs itself creates the loop which is strange since the updated loop frequency is 1Hz, just like the updates of @x themselves, not faster.

I confirm that it is working with master. Another minimal working example has been made to verify it.

Doing mix deps, we see:

* phoenix_pubsub 2.0.0-dev ( (mix)
  locked at 325abd4
* phoenix 1.5.0-dev ( (mix)
  locked at dcea155
* phoenix_live_view 0.9.0 ( (mix)
  locked at a8bf8af

In the deps part of mix.exs, I wrote:

{:phoenix, github: "phoenixframework/phoenix", override: true},
{:phoenix_live_view, github: "phoenixframework/phoenix_live_view"}

The override was necessary because it looks like the dependency specifications provided for Phoenix and for Phoenix LiveView are not coherent and cannot coexist. Maybe I should have written the deps part differently.