bunnylushington

bunnylushington

Seeking clarification on how commands and actions work together (wrt race conditions)

A question: I think i might be misunderstanding how commands and actions work together. In the code below, I would like to execute the command (changing the log level for the app) then reload the page ensuring the new log level is displayed. It appears – and I might be mistaken – that the command sometimes runs before the page refresh and sometimes after. It’s always run but sometimes I get the right current level and sometimes not. What is the idiomatic way of dealing with this in Hologram? (And apologies if I’m way off base here, I don’t do much web related programming…)

defmodule MyAppWeb.Hologram.Pages.Logging do
  @moduledoc false

  use Hologram.Page
  require Logger

  route "/logging"
  layout MyAppWeb.Hologram.Layouts.MainLayout,
         header: "Logging"

  def init(_params, component, _server) do
    component
    |> put_state(:current, Logger.level())
  end

  def action(:set_level, params, component) do
    component
    |> put_command(:set_level, params)
    |> put_page(MyAppWeb.Hologram.Pages.Logging)
  end

  def command(:set_level, params, server) do
    new_level = params.event.value
    Logger.configure(level:  String.to_atom(new_level))
    server
  end

end

Marked As Solved

bartblast

bartblast

Creator of Hologram

Here’s what’s happening and how to fix it:

The Issue

  • Commands are async - put_command/3 adds instruction to Component struct for Hologram runtime to execute after action finishes
  • Page navigations are also async - put_page/2 adds instruction to Component struct for Hologram runtime to execute after action finishes
  • When you do both from the same action, the runtime executes them in parallel - the command is sent to the server while the page is fetched from the server, causing nondeterministic behavior where either operation may finish first

Solution: Command Returns Action

Have your command return an action that navigates:

def action(:set_level, params, component) do
  # Only put the command instruction
  put_command(component, :set_level, params)
end

def command(:set_level, params, server) do
  new_level = params.event.value
  Logger.configure(level: String.to_atom(new_level))
  
  # Command returns action instruction
  put_action(server, :reload_page)
end

def action(:reload_page, _params, component) do
  put_page(component, MyAppWeb.Hologram.Pages.Logging)
end

This guarantees: Action → Command (Logger.configure) → Action (navigation)

Future Feature

put_page/2 and put_page/3 directly in commands is planned. This would allow adding the page navigation instruction directly from the command to the Server struct. This is already in the backlog and I’ll increase the priority.

This would solve these synchronization issues by allowing something like this - with no additional client-server roundtrip, the page would be rendered within the same server request just after the command:

def action(:set_level, params, component) do
  put_command(component, :set_level, params)
end

def command(:set_level, params, server) do
  new_level = params.event.value
  Logger.configure(level: String.to_atom(new_level))
  
  # Future feature - put_page directly in command (not yet implemented)
  put_page(server, MyAppWeb.Hologram.Pages.Logging)
end

or even shorter by triggering the command directly through the template without the action boilerplate:

~HOLO"""
<button $click={command: :set_level, params: %{level: :warning}}>Set level</button>
"""

Also Liked

bunnylushington

bunnylushington

Hey, thanks tons. This solves my problem (and provides better insight into how to work with Hologram generally). Appreciate you taking the time to respond.

Where Next?

Popular in Questions Top

vegabook
I’m brand new to Phoenix and I have stripped one of the demo applications to the bone. I just want to get an svg up on the screen. Here i...
New
hariharasudhan94
Lets say I have map like this fetching from my database %{"_id" =&gt; #BSON.ObjectId&lt;58eb1a7a9ad169198c3dXXXX&gt;, "email" =&gt; ...
New
_russellb
I want to try my hand at web scraping. What tools/libraries do I need to use. I’m hoping to turn this into something professional so don’...
New
skosch
To my knowledge, put_in, Map.update etc. all have the one limitation of not automatically creating intermediate keys when needed (for exa...
New
beno
I will often find my self writing things similar to: case some_value do nil -&gt; something() "" -&gt; something() _ -&gt; somethi...
New
shahryarjb
Hello, I have map which I want to convert it to string like this: the map: %{last_name: "tavakkoli", name: "shahryar"} the string I ne...
New
chokchit
** (DBConnection.ConnectionError) connection not available and request was dropped from queue after 2733ms. You can configure how long re...
New
jerry
Good day to you all. I have been struggling to get a query involving like and ilike to work. Can anyone assist me on this, please? pro...
New
pmjoe
I have a relationship of love and hate with Elixir. Lots of things are just absolutely right, but there are some things that are kind of ...
New
albydarned
Hello all! I am typing this post from my new MacBook Pro with the M1 chip. I’m loving it so far, and will probably use it as my daily dr...
New

Other popular topics Top

KronicDeth
Elixir plugin for JetBrain’s IntelliJ Platform (including Rubymine) This is a plugin that adds support for Elixir to JetBrains IntelliJ...
289 36352 110
New
chrismccord
As promised, the first release candidate of Phoenix 1.3.0 is out! This release focuses on code generators with improved project structure...
New
vonH
When I run the Plug and I recompile I wind up having to use Ctrl C to quit iex and start again. Witht the help of rlwrap I can use the cu...
New
ovidiubadita
Hey all, I discovered Elixir and I love it. I always wanted to learn a functional programming and I intended to go for Haskell, but afte...
New
aadeshere1
I have a another noob question about loop. Since elixir is immutable, while loop is not directly possible. total = 10 while total != 0 ...
New
AstonJ
We’ve put together this wiki for Phoenix LiveView - please feel free to add any info you feel is worth including. What is Phoenix LiveV...
New
gausby
I asked this very same question on twitter and got some interesting feedback, but I thought it would be a good question to ask here as we...
1207 39467 209
New
komlanvi
Hi everyone, I was playing with phoenix liveView but I run into an issue. I have a form and want to validate each input text when the te...
New
jerry
Good day to you all. I have been struggling to get a query involving like and ilike to work. Can anyone assist me on this, please? pro...
New
aesmail
Hello guys, I have finally made it. I created an admin interface for a framework. It’s been on my todo list for years and with the curre...
New

Latest on Elixir Forum

We're in Beta

About us Mission Statement