Why is my socket not updating?

I thought these 2 implementations are the same.

case Customers.update_customer(customer, params) do
      {:ok, new_customer} ->
        {:noreply,
         socket
         |> assign(:customer_form, to_form(Customers.change_customer(%Customer{})))
         |> assign(
           :customers_list,
           Enum.map(socket.assigns.customers_list, fn c ->
             if c.id == id do
               new_customer
             else
               c
             end
           end)
         )
         |> put_flash(:info, "Customer details updated!")}

      {:error, %Ecto.Changeset{} = changeset} ->
        {:noreply,
         socket
         |> assign(:customer_form, to_form(changeset))
         |> put_flash(:error, "failed to update customer details")}
    end
case Customers.update_customer(customer, params) do
      {:ok, new_customer} ->
        socket
        |> assign(:customer_form, to_form(Customers.change_customer(%Customer{})))
        |> assign(
          :customers_list,
          Enum.map(socket.assigns.customers_list, fn c ->
            if c.id == id do
              new_customer
            else
              c
            end
          end)
        )
        |> put_flash(:info, "Customer details updated!")

        {:noreply, socket}

      {:error, %Ecto.Changeset{} = changeset} ->
        {:noreply,
         socket
         |> assign(:customer_form, to_form(changeset))
         |> put_flash(:error, "failed to update customer details")}
    end

In the above example, I pipe the assigns directly in the {:noreply, socket |> …}. This works perfectly and my socket is updated. HOWEVER, when I place the piping outside of the {:noreply, socket} tuple, the socket does not update. Why is that so?

variables are immutable in elixir, so when you do the piping and everything else you’re executing an statement that has a result of a socket with the assigns, but that’s it, the result isn’t stored anywhere.

Then you do your {:noreply, socket} and the socket in this line is the socket that is passed to the function as an argument, not the one that you just assigned things.

As an small experiment you can try executing these lines in an iex shell:

iex> test = %{a: 1, b: 2, c: 3}
iex> test |> Map.put(:d, 4)
iex> test

And see what the last line show for test.

If you want to preserve the result of your operations you can use

iex> test = %{a: 1, b: 2, c: 3}
iex> test = test |> Map.put(:d, 4)
iex> test

But it is important to know that this isn’t changing the variable, but rebinding it.

This link has a nice explanation about what rebinding is.

3 Likes

Ahh, I understand! Thank you for explaining :slight_smile:

1 Like