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
Here’s what’s happening and how to fix it:
The Issue
- Commands are async -
put_command/3adds instruction toComponentstruct for Hologram runtime to execute after action finishes - Page navigations are also async -
put_page/2adds instruction toComponentstruct 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
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.
Popular in Questions
Other popular 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
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance









