# `assign_new/3` prevents expensive recomputations in LiveView

Calling `assign/2` recalculates values every time the socket updates, even if the data hasn't changed. Use `assign_new/3` to compute expensive operations only once and preserve them across LiveView updates.

```elixir
# ✅ COMPUTE ONCE - Database query runs only on first access
defmodule DashboardLive do
  use Phoenix.LiveView

  import Ecto.Query

  def mount(_params, _session, socket) do
    socket =
      socket
      |> assign_new(:categories, fn -> load_categories_from_db() end)
      |> assign(selected: nil, show_filters: false)

    {:ok, socket}
  end

  def handle_event("select_category", %{"id" => id}, socket) do
    {:noreply, assign(socket, selected: id)}
  end

  def handle_event("toggle_filter", _params, socket) do
    {:noreply, update(socket, :show_filters, &(!&1))}
  end

  defp load_categories_from_db do
    Repo.all(from c in Category, order_by: [asc: c.name])
  end
end
```

Without `assign_new/3`, every event triggers a database query for categories that never change during the session. With 20 category selections, that's 20 identical queries.

```elixir
# ❌ REPEATED WORK - Database query runs on every event
defmodule DashboardLive do
  use Phoenix.LiveView

  import Ecto.Query

  def mount(_params, _session, socket) do
    categories = load_categories_from_db()
    {:ok, assign(socket, categories: categories, selected: nil, show_filters: false)}
  end

  def handle_event("select_category", %{"id" => id}, socket) do
    # Query again!
    categories = load_categories_from_db()
    {:noreply, assign(socket, categories: categories, selected: id)}
  end

  def handle_event("toggle_filter", _params, socket) do
    # Query again!
    categories = load_categories_from_db()
    {:noreply, update(socket, :show_filters, &(!&1))}
  end

  defp load_categories_from_db do
    Repo.all(from c in Category, order_by: [asc: c.name])
  end
end
```

Now `load_categories_from_db/0` runs only once when the socket is first created. All subsequent events reuse the cached value from `socket.assigns.categories`.

## How assign_new/3 works

`assign_new/3` checks if the key already exists in socket assigns:

- **Key exists**: Skip the function, keep existing value
- **Key missing**: Run the function, store the result

This is perfect for:

- Database queries for static dropdown data
- API calls for configuration
- Expensive computations that don't change per-session
- User preferences loaded once at mount

## Works with inline child LiveViews

When a parent LiveView renders a child LiveView inline via `live_render/3`, `assign_new/3` in the child checks the parent's assigns first. If the parent already has the key, the function is skipped entirely — no redundant computation.

The child must also call `assign_new/3` with the same key. The data isn't injected automatically; the child explicitly opts in by requesting it.

```elixir
# Parent LiveView loads expensive data once
defmodule ParentLive do
  use Phoenix.LiveView

  def mount(_params, _session, socket) do
    socket = assign_new(socket, :app_config, fn -> load_app_config() end)
    {:ok, socket}
  end
end

# Child LiveView rendered inline via live_render/3 reuses parent's value
defmodule ChildLive do
  use Phoenix.LiveView

  def mount(_params, _session, socket) do
    # assign_new checks parent's assigns first — skips the function if parent already has :app_config
    socket = assign_new(socket, :app_config, fn -> load_app_config() end)
    {:ok, socket}
  end
end
```

This only applies to inline rendering (`live_render/3`). When navigating between LiveViews — even within the same `live_session` — each LiveView mounts fresh with its own process and `assign_new/3` has no access to any prior LiveView's assigns.

Use `assign_new/3` for any data that's expensive to compute and stable within a session. Your LiveView will be faster and your database will thank you.

[assign_new/3 docs](https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html#assign_new/3)


---

Created by: almirsarajcic
Date: May 20, 2026
URL: https://elixirdrops.net/d/aDFdrUhK
