-
-
Notifications
You must be signed in to change notification settings - Fork 907
One-Way data flow #192
Description
Hey all,
I love the project, really helped me a lot spinning up small prototypes very, very quickly!
I wanted to give some quick feedback, how I use it and what I could imagine might improve it further for that.
Step 1
My first step in creating something is always the same:
Think about what the user wants to see at what point of their navigation journey, i.e. describe the data they want to see at those points.
Since that data is what they want to see, it almost never what I want to store, i.e. I have duplicated data all over the place (since the same information bit often appears e.g. in 2 pages as "preview" and on a third page as "context", or more detailed.)
Example (GitHub)
Github Code Browser Page shows file name, modification date, modification author and a button to open the file.
Github File Page shows file name, file content, modification date, modification author and a button to open to the browser.
Long story short (probably not totally new so far :D ): My UIs usually involve different views onto the same data in different ways, for different User Journeys through the app.
Step 2
Now the fun is deduplicating this data to some common source of truth.
Which means I have the following data flow:
SourceOfTruth (call it "Database" or whatever)
↓ (PageDataModel.fromSourceOfTruth(..))
PageDataModel
↓ display it using view_page(data: PageDataModel) with nicegui
Browser
What I afterwards do to add interactivity is modifying the source of truth:
like db.files[page.current_file].name = xy, db.files[page.current_file].name = xy (e.g. as on_click action for a button)
Maybe then I might even add some wrapper functions, when multiple effects are caused by one user action (like def merge_pull_request_button_pressed(pr_id):)
Benefits
This style of structuring things has the obvious benefit of a common source of truth (i.e. no 'unsound' data), but also adding interactivity step by step.
The name for it is sometimes referred to as "The Elm Architecture", but Redux and the like picked up this concept as well I believe.
One thing I could imagine would benefit from building this style more into the API of nicegui could be performance and predictability.
API suggestion
@ui.data(["repo", "file", "editmode=mode"]) // <- this is data coming from the route
class RepoFile:
"""What the user want to see when they arrived at the full detailed File View in the Repo."""
last_change: LastChangeDense
file_name: FromRoute("file") # ? not type checked afterwards..
editmode: RouteParam[bool] = False # but this is
content: FileContent
def fromStorage(cls, storage: Storage, repo: str, file: str, editmode: bool):
"""Get the particular data for this view from the common source of truth (based on route parameters)."""
content = storage.repos[repo].files[file]
return cls(...)
storage = SourceOfTruth.from_blank_session()
@ui.view
def view_repo_file(p: RepoFile):
ui.row()...
nicegui.open_and_reload(view=collect_views(), source_of_truth=storage)
# the old behavior of "watch everything" could be enabled with a helper function like
# source_of_truth=globals_watcherThank you for reading my 2 cents, I'd love to hear your thoughts on this!