nicegui component pattern
This commit is contained in:
@@ -146,7 +146,9 @@ uploader = ui.upload(
|
||||
|
||||
Generate the durable storage name independently from `file.name`, keep user-uploaded active content off the application origin, and apply content-specific scanning before downstream parsers consume the file. Call `uploader.reset()` when the product should clear QUploader's client-side queue after a completed or abandoned operation.
|
||||
|
||||
## Element Updates And Refreshable Regions
|
||||
## Refreshable Component Regions
|
||||
|
||||
The reusable [component factory pattern](./architecture.md#reusable-component-contract) combines stable bindable fields with bounded structural refreshes. Use bindings and setters while an existing element can represent the change; use a refreshable region when the number, type, order, or nesting of child elements must be rebuilt.
|
||||
|
||||
Use the narrowest update mechanism that represents the change:
|
||||
|
||||
@@ -157,18 +159,55 @@ Use the narrowest update mechanism that represents the change:
|
||||
| a bounded subtree whose structure changed | `@ui.refreshable` or `@ui.refreshable_method` |
|
||||
| navigation to a different page | `ui.navigate` or `ui.sub_pages` |
|
||||
|
||||
The tagged [`refreshable` implementation](https://github.com/zauberzeug/nicegui/blob/v3.16.0/nicegui/functions/refreshable.py) records every invocation as a target container. `refresh()` clears and recreates each matching target; it does not diff children. Arguments passed to `refresh()` replace prior positional arguments when non-empty and update prior keyword arguments.
|
||||
The tagged [`refreshable` implementation](https://github.com/zauberzeug/nicegui/blob/v3.16.0/nicegui/functions/refreshable.py) records every invocation as a target containing a `RefreshableContainer`, the function arguments, and the associated object instance when applicable. The initial call both renders the region and registers that target. Calling `refresh()` before the decorated function or method has rendered does nothing because no target exists yet.
|
||||
|
||||
A module-level refreshable called by multiple clients has multiple targets, so refreshing it can update all surviving targets. Define the decorated function inside the page, create a page-local decorated wrapper, or use a per-page object with `@ui.refreshable_method` when clients need independent refresh behavior.
|
||||
For each matching target, `refresh()` clears the container, updates its remembered arguments, and invokes the function again inside that same container. It recreates the subtree rather than diffing children. Bindings and event handlers owned by deleted elements follow normal element cleanup; a retained reference to a former child does not become the new child. Expose component state and public actions through the returned component handle instead of leaking refresh-owned element references.
|
||||
|
||||
For asynchronous refreshable functions:
|
||||
### Function And Method Scope
|
||||
|
||||
Choose the decorator according to state ownership:
|
||||
|
||||
| Form | Target identity | Appropriate scope |
|
||||
| --- | --- | --- |
|
||||
| module-level `@ui.refreshable` | every surviving call target of that decorated function | deliberate multicast or shared rendering |
|
||||
| page-local `@ui.refreshable` | calls recorded by the function created during that page build | one page client |
|
||||
| page-created `ui.refreshable(function)` | calls recorded by that decorated wrapper | one page client or component factory call |
|
||||
| `@ui.refreshable_method` | targets whose recorded instance equals the accessed object | reusable component instances with independent state |
|
||||
|
||||
A module-level refreshable called by multiple clients has multiple targets, so one refresh can update every surviving target. The official [global and local scope examples](https://nicegui.io/documentation/refreshable#global_scope) demonstrate this distinction. For reusable components returned as dataclass handles, prefer a page-created instance with `@ui.refreshable_method`; NiceGUI's tagged [multi-instance tests](https://github.com/zauberzeug/nicegui/blob/v3.16.0/tests/test_refreshable.py) verify that refreshing one instance selects its own targets.
|
||||
|
||||
Calling the same refreshable function more than once creates more than one target. For `@ui.refreshable_method`, every call made on the same instance belongs to that instance, so `instance.region.refresh()` refreshes all surviving targets for that method and instance. Use separate methods or separate component instances when independently refreshing two regions is required.
|
||||
|
||||
### Arguments And Return Behavior
|
||||
|
||||
Targets remember their initial positional and keyword arguments:
|
||||
|
||||
- no refresh arguments reuse all remembered values
|
||||
- non-empty positional refresh arguments replace the remembered positional tuple
|
||||
- keyword refresh arguments update the remembered keyword dictionary
|
||||
- arguments must remain consistently positional or keyword; supplying the same parameter through both paths raises `TypeError`
|
||||
- the initial call and each refresh return the decorated function's normal result; the `refresh()` wrapper itself exposes NiceGUI's awaitable response behavior
|
||||
|
||||
Parameters should describe render input, not hide durable state. On a reusable component, fields on the returned dataclass usually provide a clearer interface than repeatedly replacing a long refresh argument list.
|
||||
|
||||
### Async Refresh
|
||||
|
||||
An async refreshable's initial invocation returns its coroutine and should be awaited when page construction depends on its output. For subsequent refreshes:
|
||||
|
||||
- `await region.refresh()` waits for all matching async refreshes to finish
|
||||
- calling `region.refresh()` without awaiting schedules the async work in the background
|
||||
- awaiting is appropriate when a button must remain disabled until rendering completes
|
||||
- each refresh clears the old target before the new async render finishes, so provide a stable outer loading surface when an empty interval would be disruptive
|
||||
|
||||
`ui.state()` is local storage indexed by call order inside one refreshable target. It can only be called inside a refreshable function, and conditional changes to state-call order can associate values with the wrong logical state. Use typed page state or bindable dataclasses when state identity must remain explicit.
|
||||
Multiple matching targets are refreshed together; awaiting waits for all async results through `asyncio.gather`. That coordinates completion but does not serialize competing refresh calls. Apply the generation, lock, or coalescing policy described under [concurrency and feedback state](#concurrency-and-feedback-state) when two operations can refresh the same target concurrently.
|
||||
|
||||
### Target And Local-State Lifetime
|
||||
|
||||
Before every invocation or refresh, NiceGUI prunes targets whose container was deleted. Clearing an ancestor, navigating away, deleting the client, or replacing an outer refreshable region can therefore remove an inner target. A later call to the inner region's `refresh()` cannot recreate a pruned outer placement; the owning outer render must invoke it again.
|
||||
|
||||
`ui.state()` stores values in a list owned by one refreshable target and identifies each value by call order. Its setter automatically refreshes the associated instance target. Conditional or reordered `ui.state()` calls can associate stored values with a different logical variable, so keep their call order stable.
|
||||
|
||||
For reusable application components, a bindable dataclass is usually the clearer state owner: fields have explicit names, can bind directly to stable elements, and remain available to the page through the returned handle. Reserve `ui.state()` for small render-local values that do not need a typed component API, cross-component coordination, service persistence, or independent tests.
|
||||
|
||||
## Timers And Application Events
|
||||
|
||||
|
||||
Reference in New Issue
Block a user