jberling
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 ![]()
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
Trending in Questions
Other Trending Topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #blog-post
- #phoenix_html
- #iex
- #graphql
- #genstage
- #ai
- #elixirconf-us
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #metaprogramming
- #hex
- #performance










First 10 of 28 Posts
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
Have you tried Live Debuger? You can inspect and trace state changes there.
jberling
Thanks for your swift reply.
There is a nested map
interviewcontaining 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
No, I’ll take a look. Looks interesting.
jberling
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
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
Ok, since handle_event for “evaluate” get the assigns value via
socket.assignssomething must have updated assigns before it is called then. Is that what you are saying?jberling
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
That’s what I meant with tracing all your LV callbacks – not just handle_event.
jberling
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.