Optimize LiveView Performance with Temporary Assigns
Rushikesh Pandit
Posted on November 26, 2024
When working with Phoenix LiveView, you often juggle a lot of data while building interactive, real-time web applications. One challenge developers face is managing memory efficiently, especially for components or pages that render frequently changing data. Temporary assigns in LiveView provide a smart way to handle this challenge.
In this blog, we’ll dive deep into what temporary assigns are, why you should use them, and provide practical examples to help you harness their power effectively.
What Are Temporary Assigns?
Temporary assigns are a feature of Phoenix LiveView that lets you specify certain assigns to be reset to their default value after every render. This means LiveView doesn’t persist these values in memory, which can save significant resources when dealing with large or frequently changing data.
Without temporary assigns, all the assigns in your LiveView process are kept in memory between renders. While this is fine for small, static data, it can quickly add up when you’re working with dynamic content, such as lists, logs, or real-time updates.
Why Use Temporary Assigns?
Here are a few scenarios where temporary assigns can be particularly helpful:
- Reducing Memory Usage: If you have a LiveView that frequently updates large datasets (e.g., a chat app or a live dashboard), temporary assigns prevent stale data from piling up in memory.
- Improving Performance: By resetting unused data after each render, you reduce the load on your LiveView processes, keeping them lightweight and efficient.
- Preventing Memory Leaks: Long-running LiveView processes with large, unused assigns can inadvertently cause memory leaks. Temporary assigns are a built-in safeguard against this.
Setting Up Temporary Assigns
To enable temporary assigns, use the temporary_assigns
key in the socket
's assign
function. Here’s a basic example:
def mount(_params, _session, socket) do
{:ok,
assign(socket,
messages: [],
temporary_assigns: [messages: []]
)}
end
In this example, we’ve marked the messages
assign as temporary. After every render, messages
will reset to an empty list ([]
), regardless of its previous state.
A Practical Example: Live Chat with Temporary Assigns
Let’s build a live chat application where messages are sent in real time. Without temporary assigns, the LiveView process would hold onto every single message, leading to bloated memory usage over time. Instead, we’ll use temporary assigns to keep the memory footprint minimal.
LiveView Module
defmodule MyAppWeb.ChatLive do
use Phoenix.LiveView
def mount(_params, _session, socket) do
{:ok,
assign(socket,
messages: [],
temporary_assigns: [messages: []]
)}
end
def handle_event("send_message", %{"content" => content}, socket) do
message = %{content: content, timestamp: DateTime.utc_now()}
# Append the new message to the messages list
{:noreply, assign(socket, messages: [message])}
end
end
LiveView Template
<div id="chat-container">
<div id="messages">
<%= for message <- @messages do %>
<div class="message">
<p><%= message.content %></p>
<small><%= message.timestamp %></small>
</div>
<% end %>
</div>
<form phx-submit="send_message">
<input type="text" name="content" placeholder="Type a message..." required />
<button type="submit">Send</button>
</form>
</div>
In this example:
-
@messages
is marked as a temporary assign, so it resets to[]
after each render. - The
messages
list is only populated during the render lifecycle, reducing the memory overhead.
Testing Temporary Assigns in Action
To see the difference temporary assigns make, you can log the state of the socket
after each event:
def handle_event("send_message", %{"content" => content}, socket) do
message = %{content: content, timestamp: DateTime.utc_now()}
updated_socket = assign(socket, messages: [message])
IO.inspect(updated_socket.assigns, label: "Socket Assigns After Render")
{:noreply, updated_socket}
end
Run the application and observe how @messages
is reset to its default state ([]
) after each render.
When Not to Use Temporary Assigns
While temporary assigns are incredibly powerful, they aren’t always the right tool. Avoid using them if:
- You need persistent data: For example, user session data or application state that shouldn’t be lost between renders.
- The cost of re-fetching data is too high: If the data comes from an external API or a complex computation, temporary assigns might introduce unnecessary overhead.
Advanced Use Cases
Combining Temporary Assigns with Streams
For large datasets that change incrementally (e.g., logs or paginated results), combine temporary assigns with LiveView streams for optimal performance.
def mount(_params, _session, socket) do
{:ok,
socket
|> assign(:logs, [])
|> stream(:logs, [])
|> assign(temporary_assigns: [logs: []])
}
end
This approach ensures that only the current batch of logs is in memory, while older logs are efficiently managed using streams.
Final Thoughts
Temporary assigns in Phoenix LiveView are an elegant solution to a common problem in real-time web development: managing memory while maintaining interactivity. By understanding when and how to use them, you can build scalable, performant applications without sacrificing user experience.
If you’ve used temporary assigns in your projects or have tips to share, let me know in the comments—I’d love to hear how you’re optimizing your LiveView apps!
Feel free to reach out if you need help.
LinkedIn: https://www.linkedin.com/in/rushikesh-pandit
GitHub: https://github.com/rushikeshpandit
Portfolio: https://www.rushikeshpandit.in
#myelixirstatus , #elixir , #phoenixframework
Posted on November 26, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.