In react, there is the concept of a “controlled component”. Typically, input
elements and the like manage their own state and respond to user input. However, sometimes it is convenient to let react store the state of the input
, so it can serve as the single source of truth.
I’ve been experiencing difficulties with liveview where it seems like the input is not controlled by the server state, despite the state changing.
Contrived example:
def mount(_params, _session, socket) do
{:ok,
assign(socket, %{
value: ""
})}
end
def handle_event("set_value", %{"key" => "ArrowUp"}, socket) do
rand = :rand.uniform(1_000_000)
{:noreply, assign(socket, value: rand)}
end
def handle_event("set_value", _params, socket) do
{:noreply, socket}
end
<div phx-window-keyup="set_value">
<input type="text" value={@value} />
</div>
The value of the input should be set the match the value of the state of the liveview. Anytime an ArrowUp
event occurs, it should select a random number, and set the value of the input to that number. This works fine when the input is not in focus, and the value in that input
element is updated accordingly.
The confusing part occurs when you click into the input such that it is in a focused state. Now, if you try to press up on the arrow pad, the handle_event
call does fire and does update the state, but the input value does not follow along. You can type whatever you want – in react it would not let you type anything, since the react state is the single source of truth and it would keep insisting that the value of the input be the random number.