Skip to content

Commit ae2d309

Browse files
Add comprehensive state mixins documentation
- Create new documentation page at docs/state/mixins.md - Explain state mixin pattern with mixin=True parameter - Include CounterMixin example from user request - Cover inheritance of vars, computed vars, and event handlers - Show multiple mixin inheritance patterns - Document backend variables in mixins - Include best practices and limitations - Follow existing reflex-web documentation patterns Co-Authored-By: [email protected] <[email protected]>
1 parent 8ad9cd8 commit ae2d309

File tree

1 file changed

+235
-0
lines changed

1 file changed

+235
-0
lines changed

docs/state/mixins.md

Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
```python exec
2+
import reflex as rx
3+
from pcweb.templates.docpage import definition
4+
```
5+
6+
# State Mixins
7+
8+
State mixins allow you to define shared functionality that can be reused across multiple State classes. This is useful for creating reusable components, shared business logic, or common state patterns.
9+
10+
## What are State Mixins?
11+
12+
A state mixin is a State class marked with `mixin=True` that cannot be instantiated directly but can be inherited by other State classes. Mixins provide a way to share:
13+
14+
- Base variables
15+
- Computed variables
16+
- Event handlers
17+
- Backend variables
18+
19+
## Basic Mixin Definition
20+
21+
To create a state mixin, inherit from `rx.State` and pass `mixin=True`:
22+
23+
```python demo exec
24+
class CounterMixin(rx.State, mixin=True):
25+
count: int = 0
26+
27+
@rx.var
28+
def count_display(self) -> str:
29+
return f"Count: {self.count}"
30+
31+
@rx.event
32+
def increment(self):
33+
self.count += 1
34+
35+
class MyState(CounterMixin, rx.State):
36+
name: str = "App"
37+
38+
def counter_example():
39+
return rx.vstack(
40+
rx.heading(MyState.name),
41+
rx.text(MyState.count_display),
42+
rx.button("Increment", on_click=MyState.increment),
43+
spacing="4",
44+
align="center",
45+
)
46+
```
47+
48+
In this example, `MyState` automatically inherits the `count` variable, `count_display` computed variable, and `increment` event handler from `CounterMixin`.
49+
50+
## Multiple Mixin Inheritance
51+
52+
You can inherit from multiple mixins to combine different pieces of functionality:
53+
54+
```python demo exec
55+
class TimestampMixin(rx.State, mixin=True):
56+
last_updated: str = ""
57+
58+
@rx.event
59+
def update_timestamp(self):
60+
import datetime
61+
self.last_updated = datetime.datetime.now().strftime("%H:%M:%S")
62+
63+
class LoggingMixin(rx.State, mixin=True):
64+
log_messages: list[str] = []
65+
66+
@rx.event
67+
def log_message(self, message: str):
68+
self.log_messages.append(message)
69+
70+
class CombinedState(CounterMixin, TimestampMixin, LoggingMixin, rx.State):
71+
app_name: str = "Multi-Mixin App"
72+
73+
@rx.event
74+
def increment_with_log(self):
75+
self.increment()
76+
self.update_timestamp()
77+
self.log_message(f"Count incremented to {self.count}")
78+
79+
def multi_mixin_example():
80+
return rx.vstack(
81+
rx.heading(CombinedState.app_name),
82+
rx.text(CombinedState.count_display),
83+
rx.text(f"Last updated: {CombinedState.last_updated}"),
84+
rx.button("Increment & Log", on_click=CombinedState.increment_with_log),
85+
rx.vstack(
86+
*[rx.text(msg) for msg in CombinedState.log_messages[-3:]] if CombinedState.log_messages else [rx.text("No logs yet")],
87+
spacing="1"
88+
),
89+
spacing="4",
90+
align="center",
91+
)
92+
```
93+
94+
## Backend Variables in Mixins
95+
96+
Mixins can also include backend variables (prefixed with `_`) that are not sent to the client:
97+
98+
```python demo box
99+
class DatabaseMixin(rx.State, mixin=True):
100+
_db_connection: dict = {} # Backend only
101+
user_count: int = 0 # Sent to client
102+
103+
@rx.event
104+
def fetch_user_count(self):
105+
# Simulate database query
106+
self.user_count = len(self._db_connection.get("users", []))
107+
108+
class AppState(DatabaseMixin, rx.State):
109+
app_title: str = "User Management"
110+
```
111+
112+
Backend variables are useful for storing sensitive data, database connections, or other server-side state that shouldn't be exposed to the client.
113+
114+
## Computed Variables in Mixins
115+
116+
Computed variables in mixins work the same as in regular State classes:
117+
118+
```python demo exec
119+
class FormattingMixin(rx.State, mixin=True):
120+
value: float = 0.0
121+
122+
@rx.var
123+
def formatted_value(self) -> str:
124+
return f"${self.value:.2f}"
125+
126+
@rx.var
127+
def is_positive(self) -> bool:
128+
return self.value > 0
129+
130+
class PriceState(FormattingMixin, rx.State):
131+
product_name: str = "Widget"
132+
133+
@rx.event
134+
def set_price(self, price: str):
135+
try:
136+
self.value = float(price)
137+
except ValueError:
138+
self.value = 0.0
139+
140+
def formatting_example():
141+
return rx.vstack(
142+
rx.heading(f"Product: {PriceState.product_name}"),
143+
rx.text(f"Price: {PriceState.formatted_value}"),
144+
rx.text(f"Positive: {PriceState.is_positive}"),
145+
rx.input(
146+
placeholder="Enter price",
147+
on_blur=PriceState.set_price,
148+
),
149+
spacing="4",
150+
align="center",
151+
)
152+
```
153+
154+
## Nested Mixin Inheritance
155+
156+
Mixins can inherit from other mixins to create hierarchical functionality:
157+
158+
```python demo box
159+
class BaseMixin(rx.State, mixin=True):
160+
base_value: str = "base"
161+
162+
class ExtendedMixin(BaseMixin, mixin=True):
163+
extended_value: str = "extended"
164+
165+
@rx.var
166+
def combined_value(self) -> str:
167+
return f"{self.base_value}-{self.extended_value}"
168+
169+
class FinalState(ExtendedMixin, rx.State):
170+
final_value: str = "final"
171+
# Inherits base_value, extended_value, and combined_value
172+
```
173+
174+
This pattern allows you to build complex functionality by composing simpler mixins.
175+
176+
## Best Practices
177+
178+
```md alert info
179+
# Mixin Design Guidelines
180+
181+
- **Single Responsibility**: Each mixin should have a focused purpose
182+
- **Avoid Deep Inheritance**: Keep mixin hierarchies shallow for clarity
183+
- **Document Dependencies**: If mixins depend on specific variables, document them
184+
- **Test Mixins**: Create test cases for mixin functionality
185+
- **Naming Convention**: Use descriptive names ending with "Mixin"
186+
```
187+
188+
## Limitations
189+
190+
```md alert warning
191+
# Important Limitations
192+
193+
- Mixins cannot be instantiated directly - they must be inherited by concrete State classes
194+
- Variable name conflicts between mixins are resolved by method resolution order (MRO)
195+
- Mixins cannot override methods from the base State class
196+
- The `mixin=True` parameter is required when defining a mixin
197+
```
198+
199+
## Common Use Cases
200+
201+
State mixins are particularly useful for:
202+
203+
- **Authentication**: Shared login/logout functionality
204+
- **Validation**: Common form validation logic
205+
- **Logging**: Centralized logging and debugging
206+
- **API Integration**: Shared HTTP client functionality
207+
- **UI State**: Common modal, loading, or notification patterns
208+
209+
```python demo box
210+
class AuthMixin(rx.State, mixin=True):
211+
is_authenticated: bool = False
212+
username: str = ""
213+
214+
@rx.event
215+
def login(self, username: str):
216+
# Simplified login logic
217+
self.username = username
218+
self.is_authenticated = True
219+
220+
@rx.event
221+
def logout(self):
222+
self.username = ""
223+
self.is_authenticated = False
224+
225+
class DashboardState(AuthMixin, rx.State):
226+
dashboard_data: list[str] = []
227+
228+
@rx.var
229+
def welcome_message(self) -> str:
230+
if self.is_authenticated:
231+
return f"Welcome, {self.username}!"
232+
return "Please log in"
233+
```
234+
235+
By using state mixins, you can create modular, reusable state logic that keeps your application organized and reduces code duplication.

0 commit comments

Comments
 (0)