A Phoenix LiveView is Just a Process
Tags: elixir, phoenix, liveview, realtime
Published: 2023-03-17 23:29:47

I want to write a quick post about [this](https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html) paragraph in the LiveView docs. It says

> A LiveView is just a process that receives events as messages and updates its state. The state itself is nothing more than functional and immutable Elixir data structures. The events are either internal application messages...or sent by the client/browser.

I didn't really get what this meant at first. It's not that this paragraph is worded poorly or anything, I'm just relatively new to Phoenix and LiveView and I had to work through an example on my own to understand it fully. I want to share that example here.

So first, a bit about Elixir processes.

Elixir apps contain one or more processes. Processes can spawn other processes. For example, if you open up an `iex` shell, you can spawn a new process that runs this greet function

```elixir
defmodule Greet do
  def greet(name) do
    IO.puts("Hello, #{name}!")
  end
end
```

with `spawn(Greet, :greet, ["friend"])`. This will print `Hello, friend!` to the console and will return the pid of the newly spawned process.

You can also send messages to processes with the `send/2` function, and those processes can receive messages with `receive`. For example, we could change our module to

```elixir
defmodule Greet do
  def greet(name) do
    receive do
      :print -> IO.puts("Hello, #{name}!")
    end
  end
end
```

Now if we spawn a new process that runs the `greet` function with `pid = spawn(Greet, :greet, ["friend"])`, we won't immediately see the message printed to the console. We can tell that process to print the message with `send(pid, :print)`.

You can also register processes with a name so you don't have to always refer to them by their pid. For example, we could run

```elixir
pid = spawn(Greet, :greet, ["friend"])
Process.register(pid, :greeter)
send(:greeter, :print)
```

Registering the pid with the name `:greeter` lets us send the message using that name instead of the pid.


So, let's bring this back to LiveView.

As that paragraph in the documentation says, "A LiveView is just a process". As such, we can send it messages, just like we could with any other process.

The cool thing about LiveView processes is that they maintain their own state, and whenever something updates that state, the LiveView process re-renders its html template and pushes the changes to the browser.

Say we have a LiveView module that looks like this (this is on version 0.18.16, btw)

```elixir
defmodule MyAppWeb.IndexLive do
  use MyAppWeb, :live_view

  attr :name, :string, default: "stranger"

  def render(assigns) do
    ~H"""
    <div>
      <p>Hello, <%= @name %>!</p>
    </div>
    """
  end

  def mount(_params, _session, socket) do
    Process.register(self(), :index)

    {:ok, socket}
  end

  def handle_info({:update_name, name}, socket) do
    socket =
      socket
      |> assign(:name, name)

    {:noreply, socket}
  end
end
```

When you visit this LiveView in your browser, `mount/3` is called, where we register the new LiveView process with the name `:index`. (If you didn't know, `self()` in Elixir refers to the pid of the process that that line is being executed by, which is, in this case, the LiveView process)

So when you visit this LiveView in your browser, you'll see the message "Hello, stranger!", and the LiveView handling your browsing session is registered under the name `:index`.

Now, a LiveView maintains its state in a variable it calls `socket`, and it responds to messages from other processes in the `handle_info` function (as opposed to the `receive` block in our earlier example). So, whenever we send a message to the LiveView process, that process calls `handle_info`, which updates its state by setting the `:name` property.

If you run your application with `iex -S mix phx.server`, then you'll be able to go into the `iex` console and send a message to the LiveView process. The only message that our LiveView process handles is one of the format `{:update_name, name}`.

So if we say `send(:index, {:update_name, "partner"})` in the console, the LiveView process will handle that message in `handle_info`, which updates the process's state, which then updates the UI in the browser to read "Hello, partner!". Pretty cool!

Going through this example helped me to grasp the fact that a LiveView is just a process that maintains your UI state. When you update that LiveView process's state, it re-renders the html and sends the diff to the browser.