| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent a43e15f commit 54878c0
23 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -36,9 +36,9 @@ The reference is split per module so each page stays scannable: | |||
| 36 | 36 | ||
| 37 | 37 | | Area | Page | Key symbols | | |
| 38 | 38 | |---|---|---| | |
| 39 | - | Element factories | [Components](components.md) | [`Text`][pythonnative.Text], [`Button`][pythonnative.Button], [`Column`][pythonnative.Column], [`Row`][pythonnative.Row], [`ScrollView`][pythonnative.ScrollView], [`FlatList`][pythonnative.FlatList], [`SectionList`][pythonnative.SectionList], [`Modal`][pythonnative.Modal], [`Pressable`][pythonnative.Pressable], [`StatusBar`][pythonnative.StatusBar], [`KeyboardAvoidingView`][pythonnative.KeyboardAvoidingView], [`RefreshControl`][pythonnative.RefreshControl], [`Picker`][pythonnative.Picker], [`ErrorBoundary`][pythonnative.ErrorBoundary] | | ||
| 40 | - | Hooks | [Hooks](hooks.md) | [`use_state`][pythonnative.use_state], [`use_reducer`][pythonnative.use_reducer], [`use_effect`][pythonnative.use_effect], [`use_memo`][pythonnative.use_memo], [`use_ref`][pythonnative.use_ref], [`use_context`][pythonnative.use_context], [`use_window_dimensions`][pythonnative.use_window_dimensions], [`use_safe_area_insets`][pythonnative.use_safe_area_insets], [`use_keyboard_height`][pythonnative.use_keyboard_height] | | ||
| 41 | - | Animations | [Animated](animated.md) | `Animated`, [`AnimatedValue`][pythonnative.AnimatedValue] | | ||
| 39 | + | Element factories | [Components](components.md) | [`Text`][pythonnative.Text], [`Button`][pythonnative.Button], [`Column`][pythonnative.Column], [`Row`][pythonnative.Row], [`ScrollView`][pythonnative.ScrollView], [`FlatList`][pythonnative.FlatList], [`SectionList`][pythonnative.SectionList], [`Modal`][pythonnative.Modal], [`Pressable`][pythonnative.Pressable], [`StatusBar`][pythonnative.StatusBar], [`KeyboardAvoidingView`][pythonnative.KeyboardAvoidingView], [`RefreshControl`][pythonnative.RefreshControl], [`Picker`][pythonnative.Picker], [`Fragment`][pythonnative.Fragment], [`ErrorBoundary`][pythonnative.ErrorBoundary] | | ||
| 40 | + | Hooks | [Hooks](hooks.md) | [`use_state`][pythonnative.use_state], [`use_reducer`][pythonnative.use_reducer], [`use_effect`][pythonnative.use_effect], [`use_memo`][pythonnative.use_memo], [`use_ref`][pythonnative.use_ref], [`use_context`][pythonnative.use_context], [`use_window_dimensions`][pythonnative.use_window_dimensions], [`use_safe_area_insets`][pythonnative.use_safe_area_insets], [`use_keyboard_height`][pythonnative.use_keyboard_height], [`memo`][pythonnative.memo] | | ||
| 41 | + | Animations | [Animated](animated.md) | `Animated`, [`AnimatedValue`][pythonnative.AnimatedValue], [`use_animated_value`][pythonnative.use_animated_value] | | ||
| 42 | 42 | | System dialogs | [Alerts](alerts.md) | [`Alert`][pythonnative.Alert] | | |
| 43 | 43 | | Platform | [Platform](platform.md) | [`Platform`][pythonnative.Platform] | | |
| 44 | 44 | | Navigation | [Navigation](navigation.md) | [`NavigationContainer`][pythonnative.NavigationContainer], [`create_stack_navigator`][pythonnative.create_stack_navigator], [`create_tab_navigator`][pythonnative.create_tab_navigator], [`create_drawer_navigator`][pythonnative.create_drawer_navigator], [`use_navigation`][pythonnative.use_navigation] | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -23,7 +23,7 @@ convenience and are documented on their canonical pages: | |||
| 23 | 23 | | [`Element`][pythonnative.element.Element] | [Element](element.md) | | |
| 24 | 24 | | [`ViewHandler`][pythonnative.native_views.base.ViewHandler] | [Native views](native_views.md) | | |
| 25 | 25 | | [`Style`][pythonnative.style.Style], [`StyleProp`][pythonnative.style.StyleProp], [`Color`][pythonnative.style.Color], [`Dimension`][pythonnative.style.Dimension], [`EdgeInsets`][pythonnative.style.EdgeInsets], [`EdgeValue`][pythonnative.style.EdgeValue], `FlexDirection`, `JustifyContent`, `Overflow`, `Position`, [`TransformSpec`][pythonnative.style.TransformSpec], [`style`][pythonnative.style.style] | [Style](style.md) | | |
| 26 | - | `parse_color_int`, `resolve_padding` | `pythonnative.native_views.base` | | ||
| 26 | + | `parse_color_int` | `pythonnative.native_views.base` | | ||
| 27 | 27 | ||
| 28 | 28 | ## Custom-component primitives | |
| 29 | 29 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -75,6 +75,12 @@ pn.Column( | |||
| 75 | 75 | - [`ErrorBoundary(child, fallback)`][pythonnative.ErrorBoundary]: | |
| 76 | 76 | catches render errors in child and displays fallback. | |
| 77 | 77 | ||
| 78 | + **Composition:** | ||
| 79 | + | ||
| 80 | + - [`Fragment(*children)`][pythonnative.Fragment]: group siblings into a | ||
| 81 | + parent's child list without an extra wrapping view (analogous to | ||
| 82 | + React's `<>…</>`). | ||
| 83 | + | ||
| 78 | 84 | **Lists:** | |
| 79 | 85 | ||
| 80 | 86 | - [`FlatList(data, render_item, key_extractor, item_height, ...)`][pythonnative.FlatList]: | |
@@ -86,7 +92,7 @@ pn.Column( | |||
| 86 | 92 | ||
| 87 | 93 | **Platform UI:** | |
| 88 | 94 | ||
| 89 | - - [`StatusBar(style, background_color, hidden)`][pythonnative.StatusBar]: | ||
| 95 | + - [`StatusBar(bar_style, background_color, hidden)`][pythonnative.StatusBar]: | ||
| 90 | 96 | configure the device's status bar (light/dark icons, color, hidden). | |
| 91 | 97 | - [`KeyboardAvoidingView(*children, behavior)`][pythonnative.KeyboardAvoidingView]: | |
| 92 | 98 | shift content up when the software keyboard appears. | |
@@ -249,6 +255,9 @@ hook state. | |||
| 249 | 255 | persists across renders. When passed via the `ref=` prop, the | |
| 250 | 256 | reconciler populates `ref["current"]` with the underlying native | |
| 251 | 257 | view. | |
| 258 | + - [`use_animated_value(initial)`][pythonnative.use_animated_value]: | ||
| 259 | + stable [`AnimatedValue`][pythonnative.AnimatedValue] across renders; | ||
| 260 | + the canonical way to drive `Animated.View`. | ||
| 252 | 261 | - [`use_context(context)`][pythonnative.use_context]: read from a | |
| 253 | 262 | context provider. | |
| 254 | 263 | - [`use_navigation()`][pythonnative.use_navigation]: navigation | |
@@ -263,6 +272,9 @@ hook state. | |||
| 263 | 272 | reactive safe-area insets. | |
| 264 | 273 | - [`use_keyboard_height()`][pythonnative.use_keyboard_height]: | |
| 265 | 274 | reactive software-keyboard height. | |
| 275 | + - [`@memo`][pythonnative.memo]: decorator that skips a function | ||
| 276 | + component's re-render when its props are shallowly equal and its | ||
| 277 | + internal state is unchanged. | ||
| 266 | 278 | ||
| 267 | 279 | ### Custom hooks | |
| 268 | 280 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -209,6 +209,17 @@ render_count = pn.use_ref(0) | |||
| 209 | 209 | render_count["current"] += 1 | |
| 210 | 210 | ``` | |
| 211 | 211 | ||
| 212 | + ### use_animated_value | ||
| 213 | + | ||
| 214 | + Create an [`AnimatedValue`][pythonnative.AnimatedValue] that's stable | ||
| 215 | + across renders. Equivalent to wrapping `pn.Animated.Value(initial)` in | ||
| 216 | + `use_memo(..., [])` but more discoverable: | ||
| 217 | + | ||
| 218 | + ```python | ||
| 219 | + opacity = pn.use_animated_value(0.0) | ||
| 220 | + pn.Animated.timing(opacity, to=1.0, duration=300).start() | ||
| 221 | + ``` | ||
| 222 | + | ||
| 212 | 223 | ### use_context | |
| 213 | 224 | ||
| 214 | 225 | Read a value from the nearest `Provider` ancestor: | |
@@ -267,6 +278,32 @@ automatically batched; the framework drains any pending re-renders | |||
| 267 | 278 | after effect flushing completes, so you don't need `batch_updates()` | |
| 268 | 279 | inside effects. | |
| 269 | 280 | ||
| 281 | + ## Memoizing function components | ||
| 282 | + | ||
| 283 | + Wrap a function component with [`@pn.memo`][pythonnative.memo] to skip | ||
| 284 | + its body when neither its props nor its internal state have changed: | ||
| 285 | + | ||
| 286 | + ```python | ||
| 287 | + @pn.memo | ||
| 288 | + @pn.component | ||
| 289 | + def ExpensiveRow(label: str, value: int): | ||
| 290 | + return pn.Row( | ||
| 291 | + pn.Text(label, style={"flex": 1}), | ||
| 292 | + pn.Text(str(value)), | ||
| 293 | + ) | ||
| 294 | + ``` | ||
| 295 | + | ||
| 296 | + When a `memo`'d component is reconciled, the reconciler compares the | ||
| 297 | + new props against the previous props using shallow equality. If they | ||
| 298 | + match and none of the component's `use_state` / `use_reducer` setters | ||
| 299 | + have fired since the last render, the previously-rendered subtree is | ||
| 300 | + reused and the component body is not re-executed. This is the | ||
| 301 | + component-level equivalent of [`use_memo`][pythonnative.use_memo]. | ||
| 302 | + | ||
| 303 | + `memo` is typically used on pure, prop-driven leaves that re-render | ||
| 304 | + frequently as part of a larger tree, e.g. rows inside a list whose | ||
| 305 | + identity doesn't change between renders of the parent. | ||
| 306 | + | ||
| 270 | 307 | ## Error boundaries | |
| 271 | 308 | ||
| 272 | 309 | Wrap risky components in | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -8,8 +8,9 @@ frame. | |||
| 8 | 8 | ||
| 9 | 9 | ## Mental model | |
| 10 | 10 | ||
| 11 | - 1. Create an [`AnimatedValue`][pythonnative.AnimatedValue] using | ||
| 12 | - [`use_memo`][pythonnative.use_memo] (so it survives re-renders). | ||
| 11 | + 1. Create an [`AnimatedValue`][pythonnative.AnimatedValue] with | ||
| 12 | + [`use_animated_value`][pythonnative.use_animated_value] (so it | ||
| 13 | + survives re-renders). | ||
| 13 | 14 | 2. Bind the value into the `style` of an `Animated.View`, | |
| 14 | 15 | `Animated.Text`, or `Animated.Image`. | |
| 15 | 16 | 3. Drive the value with `Animated.timing`, `Animated.spring`, or | |
@@ -31,7 +32,7 @@ import pythonnative as pn | |||
| 31 | 32 | ||
| 32 | 33 | @pn.component | |
| 33 | 34 | def FadeInBox(): | |
| 34 | - opacity = pn.use_memo(lambda: pn.Animated.Value(0.0), []) | ||
| 35 | + opacity = pn.use_animated_value(0.0) | ||
| 35 | 36 | ||
| 36 | 37 | def _fade_in(): | |
| 37 | 38 | pn.Animated.timing(opacity, to=1.0, duration=400).start() | |
@@ -57,7 +58,7 @@ def FadeInBox(): | |||
| 57 | 58 | ```python | |
| 58 | 59 | @pn.component | |
| 59 | 60 | def Bouncy(): | |
| 60 | - scale = pn.use_memo(lambda: pn.Animated.Value(1.0), []) | ||
| 61 | + scale = pn.use_animated_value(1.0) | ||
| 61 | 62 | ||
| 62 | 63 | def _press(): | |
| 63 | 64 | pn.Animated.spring(scale, to=1.2, stiffness=200, damping=8).start() | |
@@ -79,8 +80,8 @@ animation property. | |||
| 79 | 80 | ## Sequencing and parallel composition | |
| 80 | 81 | ||
| 81 | 82 | ```python | |
| 82 | - opacity = pn.Animated.Value(0.0) | ||
| 83 | - translate_y = pn.Animated.Value(20.0) | ||
| 83 | + opacity = pn.use_animated_value(0.0) | ||
| 84 | + translate_y = pn.use_animated_value(20.0) | ||
| 84 | 85 | ||
| 85 | 86 | pn.Animated.parallel([ | |
| 86 | 87 | pn.Animated.timing(opacity, to=1.0, duration=300), | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -55,7 +55,7 @@ Mount [`StatusBar`][pythonnative.StatusBar] anywhere in the tree (it | |||
| 55 | 55 | renders nothing visible) to control style and visibility: | |
| 56 | 56 | ||
| 57 | 57 | ```python | |
| 58 | - pn.StatusBar(style="light", background_color="#000000") | ||
| 58 | + pn.StatusBar(bar_style="light", background_color="#000000") | ||
| 59 | 59 | ``` | |
| 60 | 60 | ||
| 61 | 61 | `style` is `"light"` (light icons, dark background), `"dark"` (dark | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -38,7 +38,7 @@ def _view_showcase() -> None: | |||
| 38 | 38 | ||
| 39 | 39 | return pn.ScrollView( | |
| 40 | 40 | pn.Column( | |
| 41 | - pn.StatusBar(style="dark"), | ||
| 41 | + pn.StatusBar(bar_style="dark"), | ||
| 42 | 42 | pn.Text("Settings", style=styles["title"]), | |
| 43 | 43 | pn.Text(f"PythonNative v{pn.__version__}", style=styles["subtitle"]), | |
| 44 | 44 | pn.Text( | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -32,9 +32,9 @@ | |||
| 32 | 32 | ||
| 33 | 33 | @pn.component | |
| 34 | 34 | def AnimatedCard() -> pn.Element: | |
| 35 | - """Demonstrates ``Animated.View`` driven by ``AnimatedValue`` + ``use_memo``.""" | ||
| 36 | - opacity = pn.use_memo(lambda: pn.Animated.Value(0.0), []) | ||
| 37 | - scale = pn.use_memo(lambda: pn.Animated.Value(0.9), []) | ||
| 35 | + """Demonstrates ``Animated.View`` driven by ``use_animated_value``.""" | ||
| 36 | + opacity = pn.use_animated_value(0.0) | ||
| 37 | + scale = pn.use_animated_value(0.9) | ||
| 38 | 38 | ||
| 39 | 39 | def _enter() -> None: | |
| 40 | 40 | pn.Animated.parallel( | |
@@ -63,8 +63,11 @@ def _enter() -> None: | |||
| 63 | 63 | ) | |
| 64 | 64 | ||
| 65 | 65 | ||
| 66 | + @pn.memo | ||
| 66 | 67 | @pn.component | |
| 67 | 68 | def TypographyDemo() -> pn.Element: | |
| 69 | + """Wrapped in [`pn.memo`][pythonnative.memo] so it skips re-render when parent state changes.""" | ||
| 70 | + print("[TypographyDemo] render (should only appear once)") | ||
| 68 | 71 | return pn.Column( | |
| 69 | 72 | pn.Text("Headline", style={"font_size": 28, "font_weight": "700"}), | |
| 70 | 73 | pn.Text( | |
@@ -84,6 +87,7 @@ def TypographyDemo() -> pn.Element: | |||
| 84 | 87 | ) | |
| 85 | 88 | ||
| 86 | 89 | ||
| 90 | + @pn.memo | ||
| 87 | 91 | @pn.component | |
| 88 | 92 | def BordersAndShadows() -> pn.Element: | |
| 89 | 93 | return pn.View( | |
@@ -96,6 +100,7 @@ def BordersAndShadows() -> pn.Element: | |||
| 96 | 100 | ) | |
| 97 | 101 | ||
| 98 | 102 | ||
| 103 | + @pn.memo | ||
| 99 | 104 | @pn.component | |
| 100 | 105 | def Chips() -> pn.Element: | |
| 101 | 106 | return pn.Row( | |
@@ -112,6 +117,20 @@ def Chips() -> pn.Element: | |||
| 112 | 117 | ) | |
| 113 | 118 | ||
| 114 | 119 | ||
| 120 | + def section_heading(title: str, hint: str) -> pn.Element: | ||
| 121 | + """Compose two sibling [`pn.Text`][pythonnative.Text] nodes via [`pn.Fragment`][pythonnative.Fragment]. | ||
| 122 | + | ||
| 123 | + Returning a Fragment from a plain helper (not a ``@pn.component``) | ||
| 124 | + lets the surrounding parent (here a [`pn.Column`][pythonnative.Column]) | ||
| 125 | + flatten the siblings into its own child list without an extra | ||
| 126 | + wrapper view. | ||
| 127 | + """ | ||
| 128 | + return pn.Fragment( | ||
| 129 | + pn.Text(title, style=styles["section_title"]), | ||
| 130 | + pn.Text(hint, style=styles["hint"]), | ||
| 131 | + ) | ||
| 132 | + | ||
| 133 | + | ||
| 115 | 134 | @pn.component | |
| 116 | 135 | def ShowcaseScreen() -> pn.Element: | |
| 117 | 136 | nav = pn.use_navigation() | |
@@ -133,10 +152,10 @@ def go_back() -> None: | |||
| 133 | 152 | pn.Column( | |
| 134 | 153 | pn.Text(message, style=styles["title"]), | |
| 135 | 154 | AnimatedCard(), | |
| 136 | - pn.Text("Typography", style=styles["section_title"]), | ||
| 155 | + section_heading("Typography", "Memoized via @pn.memo; renders only once."), | ||
| 137 | 156 | TypographyDemo(), | |
| 138 | 157 | BordersAndShadows(), | |
| 139 | - pn.Text("Chips", style=styles["section_title"]), | ||
| 158 | + section_heading("Chips", "Composed via pn.Fragment without an extra container."), | ||
| 140 | 159 | Chips(), | |
| 141 | 160 | pn.Pressable( | |
| 142 | 161 | pn.View( | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -55,39 +55,59 @@ def App(): | |||
| 55 | 55 | ||
| 56 | 56 | from . import sdk | |
| 57 | 57 | from .alerts import Alert | |
| 58 | - from .animated import Animated, AnimatedValue | ||
| 58 | + from .animated import Animated, AnimatedValue, use_animated_value | ||
| 59 | 59 | from .components import ( | |
| 60 | 60 | ActivityIndicator, | |
| 61 | + ActivityIndicatorProps, | ||
| 61 | 62 | Button, | |
| 63 | + ButtonProps, | ||
| 62 | 64 | Column, | |
| 63 | 65 | ErrorBoundary, | |
| 64 | 66 | FlatList, | |
| 67 | + Fragment, | ||
| 65 | 68 | Image, | |
| 69 | + ImageProps, | ||
| 66 | 70 | KeyboardAvoidingView, | |
| 71 | + KeyboardAvoidingViewProps, | ||
| 67 | 72 | Modal, | |
| 73 | + ModalProps, | ||
| 68 | 74 | Picker, | |
| 75 | + PickerProps, | ||
| 69 | 76 | Pressable, | |
| 77 | + PressableProps, | ||
| 70 | 78 | ProgressBar, | |
| 79 | + ProgressBarProps, | ||
| 71 | 80 | RefreshControl, | |
| 72 | 81 | Row, | |
| 73 | 82 | SafeAreaView, | |
| 83 | + SafeAreaViewProps, | ||
| 74 | 84 | ScrollView, | |
| 85 | + ScrollViewProps, | ||
| 75 | 86 | SectionList, | |
| 76 | 87 | Slider, | |
| 88 | + SliderProps, | ||
| 77 | 89 | Spacer, | |
| 90 | + SpacerProps, | ||
| 78 | 91 | StatusBar, | |
| 92 | + StatusBarProps, | ||
| 79 | 93 | Switch, | |
| 94 | + SwitchProps, | ||
| 80 | 95 | Text, | |
| 81 | 96 | TextInput, | |
| 97 | + TextInputProps, | ||
| 98 | + TextProps, | ||
| 82 | 99 | View, | |
| 100 | + ViewProps, | ||
| 83 | 101 | WebView, | |
| 102 | + WebViewProps, | ||
| 84 | 103 | ) | |
| 85 | 104 | from .element import Element | |
| 86 | 105 | from .hooks import ( | |
| 87 | 106 | Provider, | |
| 88 | 107 | batch_updates, | |
| 89 | 108 | component, | |
| 90 | 109 | create_context, | |
| 110 | + memo, | ||
| 91 | 111 | use_callback, | |
| 92 | 112 | use_context, | |
| 93 | 113 | use_effect, | |
@@ -152,6 +172,7 @@ def App(): | |||
| 152 | 172 | "Column", | |
| 153 | 173 | "ErrorBoundary", | |
| 154 | 174 | "FlatList", | |
| 175 | + "Fragment", | ||
| 155 | 176 | "Image", | |
| 156 | 177 | "KeyboardAvoidingView", | |
| 157 | 178 | "Modal", | |
@@ -171,13 +192,33 @@ def App(): | |||
| 171 | 192 | "TextInput", | |
| 172 | 193 | "View", | |
| 173 | 194 | "WebView", | |
| 195 | + # Built-in Props dataclasses | ||
| 196 | + "ActivityIndicatorProps", | ||
| 197 | + "ButtonProps", | ||
| 198 | + "ImageProps", | ||
| 199 | + "KeyboardAvoidingViewProps", | ||
| 200 | + "ModalProps", | ||
| 201 | + "PickerProps", | ||
| 202 | + "PressableProps", | ||
| 203 | + "ProgressBarProps", | ||
| 204 | + "SafeAreaViewProps", | ||
| 205 | + "ScrollViewProps", | ||
| 206 | + "SliderProps", | ||
| 207 | + "SpacerProps", | ||
| 208 | + "StatusBarProps", | ||
| 209 | + "SwitchProps", | ||
| 210 | + "TextInputProps", | ||
| 211 | + "TextProps", | ||
| 212 | + "ViewProps", | ||
| 213 | + "WebViewProps", | ||
| 174 | 214 | # Core | |
| 175 | 215 | "Element", | |
| 176 | 216 | "create_screen", | |
| 177 | 217 | # Hooks | |
| 178 | 218 | "batch_updates", | |
| 179 | 219 | "component", | |
| 180 | 220 | "create_context", | |
| 221 | + "memo", | ||
| 181 | 222 | "use_callback", | |
| 182 | 223 | "use_context", | |
| 183 | 224 | "use_effect", | |
@@ -225,6 +266,7 @@ def App(): | |||
| 225 | 266 | # Animation | |
| 226 | 267 | "Animated", | |
| 227 | 268 | "AnimatedValue", | |
| 269 | + "use_animated_value", | ||
| 228 | 270 | # Imperative | |
| 229 | 271 | "Alert", | |
| 230 | 272 | # Native modules | |
| Back | FazBrowse Home | New Git URL |
0 commit comments