user20230119

user20230119

Is Using Nested LiveComponents and Process Dictionary an Antipattern in LiveView?

I’m currently working on a LiveView project where I have a fairly complex UI with nested components. Specifically, I’m using nested LiveComponents to represent hierarchical data structures. Additionally, I’m leveraging the Process Dictionary (Process.put/2 and Process.get/2) to store and retrieve some state, such as the currently selected child entity.

Here’s a simplified example of what I’m doing:

# LiveComponent
~H"""
...
<.button phx-click="select_child" phx-value-id={@entity.id}>Select</.button>
<.button phx-click="link_child" phx-target={@myself} phx-value-path="parent">Link</.button>
...
"""

def handle_event("link_child", %{"links" => path} = params, socket) do
  id = case Process.get(:selected_child) do
    nil -> ""
    child -> child.id
  end

  socket =
    with {:ok, entity} <-
      MyApp.Entity.update_entity_links(socket.assigns.entity,%{path: path |> List.wrap(), value: id})
      do
        socket |> assign(:entity, entity)
    else
      {:error, error} ->
        error |> dbg()
        socket
    end

  {:noreply, socket}
end
# LiveView
def handle_event("select_child", %{"id" => id}, socket) do
  socket =
    with {:ok, child} <- MyApp.Entity.get_entity_by_id(id) do
      Process.put(:selected_child, child)
      socket |> assign(:selected_child, child)
    else
      {:error, error} ->
        error |> dbg()
        socket
    end

  {:noreply, socket}
end

The nested LiveComponents are used to render and manage parts of the UI, and the Process Dictionary is used to share state between different parts of the LiveView. Are there any pitfalls I should be aware of, or alternative approaches you’d recommend?

Most Liked

garrison

garrison

A simple answer would be that it’s not idiomatic in LiveView. The comment above points out the change tracking issues.

There are push-based and pull-based reactive systems (really it’s more of a spectrum). LiveView is more on the push side of things - assigns are marked dirty at assign() time and then the dynamics are re-sent in a batch computation. The point being: diffing actually happens when you call assign(), not at render time. In React for example it’s closer to the other way around (though not exactly - they are both somewhere in the middle of the “spectrum”).

If you start storing state outside of the LiveView system you are playing with fire. Technically in the examples from the OP the assigns are not actually used in the template so it should work, but in practice you often want to update your UI based on what item is selected. If you are passing data around via the process dictionary you can no longer propagate information about which assigns are dirty in each render, and the UI will no longer update properly. It’s bad practice to go down this path.

I’m sure there are situations where if you know exactly what you’re doing you could get away with this, but the OP is asking whether it’s an antipattern, and the answer to that is yes, absolutely.

And I know you’re probably aware of this, but for anyone new who’s happening across this thread: we generally try to avoid using the process dictionary in general. It’s an escape hatch which is there for when you really need it.

garrison

garrison

Nesting LiveComponents is fine.

Using the process dictionary like that, though, is a very bad idea. You should pass state down explicitly through assigns. If you need to send information between components you can use send() and send_update().

See “Managing state” in the docs.

Schultzer

Schultzer

Why is it a bad idea?

Where Next?

Popular in Questions Top

sen
Hi All, I set a environment variables in dev.exs , like below code. when i start server, how can i set the ${enable} value? thanks. d...
New
myronmarston
The Elixir Typespec docs show the following syntax for keyword lists in typespecs: # ... | [key: type] # keyword lists...
New
shahryarjb
Hello, I get Persian date from my client and convert it to normal calendar like this: def jalali_string_to_miladi_english_number(persi...
New
earth10
Hi, I’m just starting to build a side-project with Elixir and Phoenix and doing some basic test with Elixir alone. What strikes me is th...
New
stefanchrobot
What’s the safe way to decode a JSON string into a struct? I want to avoid calling String.to_atom. Jason.decode can give me a map with st...
New
ycv005
I have followed this StackOverflow post to install the specific version of Erlang. And When I am running mix ecto.setup then getting fol...
New
fayddelight
I tried installing elixir 1.11.2 erlang 23.3.4 via asdf in my zsh shell. Enabled the versions locally and globally. When I list them ...
New
script
If I have a string “1000 cfu/ml” . I want to remove the characters and / and space . So the string is like this "1000" What is the ...
New
svb
Hi! Currently I want to submit a form by pressing the Enter key. However, since my input field is of type “textarea” this is just adds a...
New
lanycrost
Hi everyone! I need implement if…else if…else condition from my elixir code, and anymore of this control flow structures not work proper...
New

Other popular topics Top

sorentwo
Hello! tl;dr Announcing Oban, an Ecto based job processing library with a focus on reliability and historical observability. After spen...
985 42920 311
New
AstonJ
Posting this to see if we can make things easier for people to get into Neovim. If you use Neovim and have a favourite distro please let ...
New
greenz1
I have a phoenix application from which a user can download multiple(5-6) files of size 1MB. I couldn’t find anything related to sending ...
New
Patoshizzle
After calling mix ecto.create I get this error: 17:00:32.162 [error] GenServer #PID&lt;0.412.0&gt; terminating ** (Postgrex.Error) FATAL...
New
JeremM34
Hello, how can I check the Phoenix version ? Thanks !
New
chrismccord
Phoenix 1.4.0 released Phoenix 1.4 is out! This release ships with exciting new features, most notably with HTTP2 support, improved deve...
688 30877 112
New
msaraiva
Surface is an experimental library built on top of Phoenix LiveView and its new LiveComponent API that aims to provide a more declarative...
564 43622 214
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
fayddelight
I tried installing elixir 1.11.2 erlang 23.3.4 via asdf in my zsh shell. Enabled the versions locally and globally. When I list them ...
New
klo
Got a question about when to concat vs. prepending items to list then reversing to achieve appending. So i know lists boil down to [1 | ...
New

We're in Beta

About us Mission Statement