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
classCounterView(LiveView):
defmount(self, request, **kwargs):
self.count = 0 # This is state
self.message = "Hello" # This is also state
defincrement(self):
self.count += 1 # Triggers re-render
Declarative State
Use the @state decorator for cleaner syntax:
fromdjustimport LiveView, state, event
classProfileView(LiveView):
# State with default values
first_name = state(default='')
last_name = state(default='')
email = state(default='')
@event
defupdate_name(self, first: str = '', last: str = '', **kwargs):
self.first_name = first
self.last_name = last
Computed Properties
Derive values from state:
fromdjustimport LiveView, state, computed
classCartView(LiveView):
items = state(default=[])
tax_rate = state(default=0.08)
@computed
defsubtotal(self):
return sum(item['price'] * item['qty'] for item in self.items)
@computed
deftax(self):
return self.subtotal * self.tax_rate
@computed
deftotal(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
classFeedView(LiveView):
defmount(self, request, **kwargs):
# Initialize stream with items
self.stream('posts', Post.objects.all()[:100])
defadd_post(self, content):
post = Post.objects.create(content=content)
self.stream_insert('posts', post) # Append
defremove_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.
classChatView(LiveView):
# Clear messages from memory after each render
temporary_assigns = {'messages': []}
defmount(self, request, **kwargs):
self.messages = Message.objects.all()[:50]
defnew_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