State Management
Learn how to manage state in djust LiveViews for reactive updates.
State Basics
In djust, state lives on the server. Any instance attribute on your LiveView becomes state that's tracked and triggers re-renders when changed.
Simple State
class CounterView(LiveView):
def mount(self, request, **kwargs):
self.count = 0 # This is state
self.message = "Hello" # This is also state
def increment(self):
self.count += 1 # Triggers re-render
Declarative State
Use the @state decorator for cleaner syntax:
from djust import LiveView, state, event_handler
class ProfileView(LiveView):
# State with default values
first_name = state(default='')
last_name = state(default='')
email = state(default='')
@event_handler
def update_name(self, first: str = '', last: str = '', **kwargs):
self.first_name = first
self.last_name = last
Computed Properties
Derive values from state:
from djust import LiveView, state, computed
class CartView(LiveView):
items = state(default=[])
tax_rate = state(default=0.08)
@computed
def subtotal(self):
return sum(item['price'] * item['qty'] for item in self.items)
@computed
def tax(self):
return self.subtotal * self.tax_rate
@computed
def total(self):
return self.subtotal + self.tax
Streams
Streams are memory-efficient collections for large datasets. Items are cleared from server memory after each render, but the client preserves DOM elements.
Basic Usage
class FeedView(LiveView):
def mount(self, request, **kwargs):
# Initialize stream with items
self.stream('posts', Post.objects.all()[:100])
def add_post(self, content):
post = Post.objects.create(content=content)
self.stream_insert('posts', post) # Append
def remove_post(self, post_id: int):
Post.objects.filter(id=post_id).delete()
self.stream_delete('posts', post_id)
Template
<ul dj-stream="posts">
{% for post in streams.posts %}
<li id="posts-{{ post.id }}">{{ post.content }}</li>
{% endfor %}
</ul>
Stream Operations
| Method | Description |
|---|---|
stream(name, items) |
Initialize or update stream |
stream_insert(name, item, at=-1) |
Insert item (-1=end, 0=start) |
stream_delete(name, item_or_id) |
Remove item by ID or object |
Temporary Assigns
For large collections without streams, use temporary_assigns to
clear data from server memory after each render.
class ChatView(LiveView):
# Clear messages from memory after each render
temporary_assigns = {'messages': []}
def mount(self, request, **kwargs):
self.messages = Message.objects.all()[:50]
def new_message(self, content):
msg = Message.objects.create(content=content)
self.messages = [msg] # Only new message sent
Template with Append
<ul dj-update="append" id="messages">
{% for msg in messages %}
<li id="msg-{{ msg.id }}">{{ msg.content }}</li>
{% endfor %}
</ul>
Best Practices
Keep State Minimal
# Good
self.selected_ids = {1, 2, 3}
# Bad
self.selected_items = [obj1, obj2, obj3]
Use Streams for Large Collections
# Good
self.stream('items', Item.objects.all()[:100])
# Bad
self.items = list(Item.objects.all()[:100])
Batch State Updates
# Good
self.items = items # Single update
# Bad
for item in items: self.items.append(item) # Multiple re-renders!
Next Steps
- Forms - Form state management
- Testing - Testing state changes
- API Reference - State API documentation