Auto-memoize stateful components with experimental-memo wrappers. Registered in ``default_page_plugins`` before ``DefaultCollectorPlugin``. Components either render as passthrough memo wrappers or snapshot memo wrappers (see ``get_memoization_strategy``): - Snapshot wrappers (``Mem
| 201 | |
| 202 | @dataclasses.dataclass(frozen=True, slots=True) |
| 203 | class MemoizeStatefulPlugin(Plugin): |
| 204 | """Auto-memoize stateful components with experimental-memo wrappers. |
| 205 | |
| 206 | Registered in ``default_page_plugins`` before ``DefaultCollectorPlugin``. |
| 207 | Components either render as passthrough memo wrappers or snapshot memo |
| 208 | wrappers (see ``get_memoization_strategy``): |
| 209 | |
| 210 | - Snapshot wrappers (``MemoizationLeaf``-style boundaries and structural |
| 211 | ``Foreach`` wrappers): wrapped in ``enter_component`` |
| 212 | and returned with empty structural children. The walker skips descent, so |
| 213 | hooks attached to the captured body are compiled into the memo body only. |
| 214 | - Passthrough wrappers are wrapped in |
| 215 | ``leave_component`` after descendants have already compiled, so any inner |
| 216 | memo wrappers flow into this wrapper's children. |
| 217 | |
| 218 | Descendants of a snapshot boundary are never independently memoized; the |
| 219 | boundary owns the wrapping decision for its whole subtree. This is tracked |
| 220 | via ``PageContext.memoize_suppressor_stack`` — a stack of component ids |
| 221 | that pushed suppression, popped in ``leave_component`` when the matching |
| 222 | component leaves. |
| 223 | """ |
| 224 | |
| 225 | def enter_component( |
| 226 | self, |
| 227 | comp: BaseComponent, |
| 228 | /, |
| 229 | *, |
| 230 | page_context: PageContext, |
| 231 | compile_context: Any, |
| 232 | in_prop_tree: bool = False, |
| 233 | ) -> BaseComponent | ComponentAndChildren | None: |
| 234 | """Memoize snapshot-boundary subtrees before descent. |
| 235 | |
| 236 | Snapshot boundaries (``MemoizationLeaf``-style, see |
| 237 | ``is_snapshot_boundary``) stash state-referencing hooks inside |
| 238 | internally-built structural children. If we waited until |
| 239 | ``leave_component`` to swap the boundary for its memo wrapper, the |
| 240 | walker would have already descended and the collector plugin would |
| 241 | have pulled those hooks into page scope. Returning the wrapper with |
| 242 | empty structural children here causes the walker to skip the descent |
| 243 | entirely — the boundary's full snapshot lives only in the memo |
| 244 | component definition compiled separately. |
| 245 | |
| 246 | Non-boundary components are handled in ``leave_component`` so their |
| 247 | already-compiled children flow into the wrapper. |
| 248 | |
| 249 | Args: |
| 250 | comp: The component being visited. |
| 251 | page_context: The active page context. |
| 252 | compile_context: The active compile context. |
| 253 | in_prop_tree: Whether the component is in a prop subtree. |
| 254 | |
| 255 | Returns: |
| 256 | A ``(wrapper, ())`` replacement for memoized boundaries, otherwise |
| 257 | ``None``. |
| 258 | """ |
| 259 | if in_prop_tree: |
| 260 | return None |
no outgoing calls