diff --git a/plugins/ui/docs/_assets/your-ui-as-a-tree1.png b/plugins/ui/docs/_assets/your-ui-as-a-tree1.png new file mode 100644 index 000000000..5d572df7f Binary files /dev/null and b/plugins/ui/docs/_assets/your-ui-as-a-tree1.png differ diff --git a/plugins/ui/docs/_assets/your-ui-as-a-tree2.png b/plugins/ui/docs/_assets/your-ui-as-a-tree2.png new file mode 100644 index 000000000..a892f0651 Binary files /dev/null and b/plugins/ui/docs/_assets/your-ui-as-a-tree2.png differ diff --git a/plugins/ui/docs/describing/your-ui-as-a-tree.md b/plugins/ui/docs/describing/your-ui-as-a-tree.md new file mode 100644 index 000000000..1d277c9bb --- /dev/null +++ b/plugins/ui/docs/describing/your-ui-as-a-tree.md @@ -0,0 +1,136 @@ +# Your UI as a Tree + +Your `deephaven.ui` query is taking shape with many components being nested within each other. How does `deephaven.ui` keep track of your query's component structure? + +`deephaven.ui`, and many other UI libraries, model UI as a tree. This approach helps clarify the relationships between components and aids in debugging issues related to performance and state management. + +## UI as a tree + +Trees are a relationship model between items and UI is often represented using tree structures. For example, browsers use tree structures to model HTML (DOM) and CSS (CSSOM). Mobile platforms also use trees to represent their view hierarchy. + +```mermaid +flowchart LR + subgraph UI-Tree + A --> B + A --> C + end + X[ + Component A + Component B + Component C + ] + X --> UI-Tree --> DOM@{ shape: doc, label: "DOM" } +``` + +Like browsers and mobile platforms, `deephaven.ui` also uses tree structures to manage and model the relationship between components in a `deephaven.ui` query. These trees are useful tools to understand how data flows through a `deephaven.ui` query and how to optimize rendering. + +## The render tree + +A major feature of components is the ability to compose components of other components. As we nest components, we have the concept of parent and child components, where each parent component may itself be a child of another component. + +When we render a `deephaven.ui` app, we can model this relationship in a tree, known as the render tree. + +Here is a `deephaven.ui` app that renders random numbers. + +```python +from deephaven import ui +import random + + +@ui.component +def random_generator(): + num, set_num = ui.use_state() + + def generate(): + set_num(random.random()) + + return ui.flex( + ui.text(f"Random Number: {num}"), + ui.button("Generate random number", on_press=generate), + direction="column", + ) + + +@ui.component +def app(): + return ui.flex( + ui.heading("Random Number Generator"), + random_generator(), + direction="column", + ) + + +my_app = app() +``` + +![my_app](../_assets/your-ui-as-a-tree1.png) + +```mermaid +flowchart TD + A[app] -->|renders| B[ui.flex] + B -->|renders| C[ui.heading] + B -->|renders| D[random_generator] + D -->|renders| E[ui.flex] + E -->|renders| F[ui.text] + E -->|renders| G[ui.button] +``` + +From the example app, we can construct the above render tree. Each arrow in the tree points from a parent component to a child component. + +A render tree represents a single render pass of a `deephaven.ui` component. With conditional rendering, a parent component may render different children depending on the data passed. + +We can update the app to conditionally render either a `ui.text` or a `ui.heading`. + +```python +from deephaven import ui +import random + + +@ui.component +def random_generator(): + num, set_num = ui.use_state() + flag, set_flag = ui.use_boolean() + + def generate(): + set_flag.toggle() + set_num(random.random()) + + return ui.flex( + ui.text(f"Random text: {num}") + if flag + else ui.heading(f"Random heading: {num}"), + ui.button("Generate random number", on_press=generate), + direction="column", + ) + + +@ui.component +def app(): + return ui.flex( + ui.heading("Random Number Generator"), + random_generator(), + direction="column", + ) + + +my_app = app() +``` + +![my_app](../_assets/your-ui-as-a-tree2.png) + +```mermaid +flowchart TD + A[app] -->|renders| B[ui.flex] + B -->|renders| C[ui.heading] + B -->|renders| D[random_generator] + D -->|renders| E[ui.flex] + E -->|renders?| F[ui.text] + E -->|renders?| G[ui.heading] + E -->|renders| H[ui.button] +``` + +In this example, depending on the `flag` , we may render `ui.text` or `ui.heading`. The render tree may be different for each render pass. + +Although render trees may differ across render passes, these trees are generally helpful for identifying what the top-level and leaf components are in a `deephaven.ui` component. Top-level components are those nearest to the root component and affect the rendering performance of all the components beneath them and often contain the most complexity. Leaf components are near the bottom of the tree and have no child components. They are often frequently re-rendered. + +Identifying these categories of components is useful for understanding the data flow and performance of your component. diff --git a/plugins/ui/docs/sidebar.json b/plugins/ui/docs/sidebar.json index d0f924735..85cd16769 100644 --- a/plugins/ui/docs/sidebar.json +++ b/plugins/ui/docs/sidebar.json @@ -56,6 +56,10 @@ { "label": "Pure Components", "path": "describing/pure_components.md" + }, + { + "label": "Your UI as a Tree", + "path": "describing/your-ui-as-a-tree.md" } ] },