Skip to content

One-Way data flow #192

@a-teammate

Description

@a-teammate

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_watcher

Thank you for reading my 2 cents, I'd love to hear your thoughts on this!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions