jberling

jberling OP

I’m developing a LiveView with a somewhat complex, nested state. I’m encountering a strange bug: When handle_event is called for some events, the socket argument is filled with stale data. I find it hard to debug, as the machinery behind LiveView is pretty opaque to me.

I have struggled with this for half a day, or something like that, and feel like a junior developer again :slight_smile:

Now I wonder: how do you debug LiveViews? Are there any tools one can use?

Can I inspect the state? Since I’ve encountered a bug that seems to indicate stale data, where can I inspect the stored data? If, indeed, data is stored in several places, where? Is it stored in the view hierarchy somehow?

Another possibility is that the data is updated before the event handler with the “stale” data is
called. But I have not found any indications this is the case.

I can’t show you all the code, but below are the parts of the LiveView structure that I think are important.

def render(assigns) do
  ~H"""
  <main>
    # Could this be the source of the problem? I've thought of putting the answer_editor in a LiveView component
    # in some kind of shotgun debugging fashion. I have no idea why it would help, but maybe it would be easier to debug
    # since I could encapsulate the state changes in the component.
	# Or it would be harder to reason around because of the extra complexity.
    <%= for qa <- @question_items do %>
      <.answer_editor
        form={qa.form}
        variant={qa.variant}
        question={qa.question}
        open?={qa.open?}
        evaluation={qa.evaluation}
      />
    <% end %>
  </main>
  """
end
def answer_editor(assigns) do
  ~H"""
  <.simple_form for={@form} phx-change="update_editor" phx-submit="evaluate">
    . . .
    <.input type="textarea" field={@form[:answer]} phx-debounce="750" />
    . . .
    <.button name="action" value="evaluate_now">Bedöm</.button>
  </.simple_form>
  """
end
def handle_event(
	"evaluate",
	%{"question_id" => question_id},
	socket
	) do
  # This logs stale data
  IO.inspect(socket.assigns.interview.answers)
  . . .
end
def handle_event(
  "update_editor",
  %{"question_id" => question_id, "answer" => answer},
  socket
  ) do
  # This logs the expected data
  IO.inspect(socket.assigns.interview.answers)
  . . .
end

First 10 of 28 Posts Switch mode

LostKobrakai

LostKobrakai

Nested in terms of deeply nested maps or nested in terms of live components nested within the live view?

LiveViews run on processes, specifically they’re powered by a GenServer. So you can use all the tools you have at your disposal for inspecting what a process does over time.

Specifically I want to point at tracing (e.g. Extrace library) and :sys.get_state/1.

However outside of the callbacks you have in your live view module nothing should ever touch your assigns. So if you put e.g. logs or inspects into all of your callbacks you should also be able to trace all changes to assigns and when/how they’re modified. LV itself won’t though them (generally, e.g. streams are touched, but that’s part of their featureset).

dimamik

dimamik

Have you tried Live Debuger? You can inspect and trace state changes there.

jberling

jberling OP

Thanks for your swift reply.

There is a nested map interview containing questions, answers, and evaluations, which in turn are maps. From this map I derive a list of maps for each question that is used when I actually render things.

I have not used live components yet, but maybe I should? I’ve read several warnings about using them, but maybe this is a good use case?

I’ll take a look at it.

Ok, I’ve tried to do this, but I’ll make another try.

jberling

jberling OP

No, I’ll take a look. Looks interesting.

jberling

jberling OP

No, I can’t find anything that changes the state before “evaluate” is called, but it is called with stale data. Could it be that the socket data is stored in several places after all? Maybe cached?

The current solution is the result of exploring a solution for a complex problem. I’ll try to rebuild it more elegantly. But it bugs me I couldn’t find out why it doesn’t work the way I expected.

LostKobrakai

LostKobrakai

No. The socket is stored in the process heap and only the process itself has access to that. In theory the LV code running around your LV callback module would have access, but again it isn’t meant to touch assigns. It has no knowledge about the data you store anyways – it could literally be anything.

jberling

jberling OP

Ok, since handle_event for “evaluate” get the assigns value via socket.assigns something must have updated assigns before it is called then. Is that what you are saying?

jberling

jberling OP

I’m pretty sure now, that for some reason mount is called before the event_handler. I added a random value that only is added in mount, and it is added. This is why I don’t get the expected data.

LostKobrakai

LostKobrakai

That’s what I meant with tracing all your LV callbacks – not just handle_event.

jberling

jberling OP

Still, on further investigation, mount is not called before handle_event. I added IO.puts("PONGDONGPONG!!!!!!!!!!!!!!!!!!!!!!!") at the beginning of mount, and it’s not in the log.

This is so confusing. Something is going on that doesn’t fit my mental model.

My code is a mess. It’s hard to reason about. I’ll rebuild the view.

Thanks anyway for the help.

Where Next? Top

Trending in Questions Top

stjefim
Hello! Suppose you are building workflow (order / task / payment) processing system with the following requirements: Each workflow con...
New
jonnycharles
I’m in search of an Elixir library that offers PDF generation capabilities similar to Ruby’s Prawn. While there have been discussions abo...
New
spammy
I’m looking to build a personal workflow to quickly deploy web applications written in elixir/phoenix, for local consumption (ie not on t...
New
silverdr
Using Phoenix.LiveView.TagEngine as an EEx.Engine is deprecated! To compile HEEx, use Phoenix.LiveView.TagEngine.compile/2 instead. Sta...
New
dli
Before I dive in myself, did anyone successfully sprinkle Hologram into their existing LiveView app? Looking for hints regarding: Addi...
New
bottlenecked
Hi all, I wanted to ask how the community is dealing with post-release steps. Today we have Ecto migrations, which make sure that the db...
New
roeland
Kia ora, We have been using elixir-google-api to connect to Google Drive. However, with the updates to Tesla due to CVEs this is now bro...
New

Other Trending Topics Top

JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge &amp; Solve. They are GUI (Emerge) and State management (S...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
ausimian
Emily is an Elixir library that runs Nx computations on Apple’s MLX. Install it as the default Nx backend and Nx, defn, Axon, Nx.Serving,...
New
type1fool
I just stumbled on a newly redesigned elixir-lang.org. :tada: It looks like @Software_Mansion did the work, and I think it is generally a...
New

We're in Beta

About us Mission Statement