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