Managing state #4758
Replies: 2 comments 8 replies
-
Hello @FabianGoessling. Let me help you as much as possible in getting over the NiceGUI learning curve. First of all, unlike Reflex, our front-end and back-end is much more "close together". Our page functions BOTH does back-end calculations, as well as populating the front-end with This is an important realization to make. This is because, if I am not mistaken, in Reflex, you're not supposed to return UI elements in yout However, in NiceGUI, you can imagine each page as one Python function, which just so happens to have some So, in short, I'd say Reflex does full-stack by having you be both a front-end and a back-end engineer, while NiceGUI let any browser be the frontend of your Python code while you focus on the backend only. From what I see, 3 choices:
@ui.page('/hello')
def hello_page():
hello_string = generate_hello_string() # see wht I did here
ui.label(hello_string) This way, on every page load, the string is loaded in
from nicegui import ui, app
app.storage.general['hello_string'] = generate_hello_string() # see wht I did here
@ui.page('/hello')
def hello_page():
ui.label().bind_text_from(app.storage.general, 'hello_string') # see wht I did here
from nicegui import ui, app
@ui.page('/hello')
def hello_page():
ui.input(load_from_SQLite(), on_change=lambda e: save_to_SQLite(e.value)) However, you need to be more specific for me to provide any more specific recommendations. If you don't mind, you can open-source your application, then me and others will raise issues and PRs if I have spare time. You may even get featured on the NiceGUI examples! Alternatively, if you'd like not to do that, you would need to boil-down your code to only the problematic parts and provide a MRE. https://github.com/zauberzeug/nicegui/wiki/FAQs#why-is-it-important-to-provide-a-minimal-reproducible-example |
Beta Was this translation helpful? Give feedback.
-
Hey @FabianGoessling, I stumbled upon your discussion about state management in NiceGUI, and I thought my library reaktiv might help with this issue. I'm not a NiceGUI expert by any means, but I have extensive experience with Angular and similar reactive frameworks. When I faced similar state management challenges in my Python applications, I ended up creating Here's how I've been using it with NiceGUI in my internal tools: # install reaktiv with `pip install reaktiv`
from reaktiv import Signal, Computed, Effect
from nicegui import ui
# State module - completely independent from UI
class TodoState:
def __init__(self):
self.todos = Signal([])
self.filter = Signal("all") # all, active, completed
self.filtered_todos = Computed(lambda: [
todo for todo in self.todos()
if self.filter() == "all"
or (self.filter() == "active" and not todo["completed"])
or (self.filter() == "completed" and todo["completed"])
])
self.active_count = Computed(lambda:
sum(1 for todo in self.todos() if not todo["completed"])
)
self.completed_count = Computed(lambda:
sum(1 for todo in self.todos() if todo["completed"])
)
def add_todo(self, text):
self.todos.update(lambda todos: todos + [{"text": text, "completed": False}])
def toggle_todo(self, index):
self.todos.update(lambda todos: [
{**todo, "completed": not todo["completed"]} if i == index else todo
for i, todo in enumerate(todos)
])
def clear_completed(self):
self.todos.update(lambda todos: [todo for todo in todos if not todo["completed"]])
# Create a state instance
state = TodoState()
# UI layer can now use the state
with ui.card():
ui.label("Todo App").classes("text-xl")
# Input for new todos
with ui.row():
new_todo = ui.input("New task")
ui.button("Add", on_click=lambda: [state.add_todo(new_todo.value), new_todo.set_value("")])
# Todo list - connected to state via Effect
todo_container = ui.column()
def render_todos():
todo_container.clear()
for i, todo in enumerate(state.filtered_todos()):
with todo_container:
with ui.row():
ui.checkbox(value=todo["completed"], on_change=lambda e, idx=i: state.toggle_todo(idx))
ui.label(todo["text"]).classes("line-through" if todo["completed"] else "")
# Effect connects state to UI
render_effect = Effect(render_todos)
# Filter controls
with ui.row():
ui.button("All", on_click=lambda: state.filter.set("all"))
ui.button("Active", on_click=lambda: state.filter.set("active"))
ui.button("Completed", on_click=lambda: state.filter.set("completed"))
ui.button("Clear completed", on_click=lambda: state.clear_completed())
# Status display - automatically updates
status_label = ui.label()
status_effect = Effect(lambda: status_label.set_text(
f"{state.active_count()} active, {state.completed_count()} completed"
))
ui.run() What I love about this approach is that I've completely separated my state logic from the UI. I hope this will be interesting for some people. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Hey there,
i am using nicegui for almost three years now, but am still struggling with properly managing my application state in a clean and concise way.
Prior to using nicegui I was building the frontend using vue.js and the backend using fastapi/Django. The split between frontend and backend of course made coding way more complicated but I was always sure where my variables "lived" - some in my vue.js data prop and some in the backend.
Using nicegui my code always ends up in a situation where I cannot use the backend without frontend and vice versa. This contradicts the idea that my backend is setup as an API, which should be usable (and testable) standalone.
Recently I read the documentation of reflex.dev and they are very clear on the matter of the application state. They have a state class which lives in the backend side and is automatically refreshed/bound.
How would i manage state best in your opinion? How are you doing it for larger apps? Can I adopt the reflex pattern with nicegui or is there a conceptional difference?
Moreover I am wondering if there is a possibility to create variables which are only on the frontend side (e.g. an "is_visible" or an "is_calculating"). Is it possible or are all variables always updated in the backend?
Looking forward to your approaches,
Thanks
Fabian
Beta Was this translation helpful? Give feedback.
All reactions