Mutations, Keys & PjxContext¶
Public API for reactive state keys, mutation tracking, request-scoped load context, and development guardrails.
See Reactivity for conceptual documentation.
MutationKey¶
Base class for app-level reactive key constants. Subclass and declare members; use the members in react={...} and @mutates — all normalize to their string values. Both react= and @mutates only accept MutationKey members; passing a bare string raises TypeError.
PjxKey¶
Marker for Annotated[..., PjxKey()]. Keyed components declare exactly one PjxKey field; its value is stamped as data-pjx-load on render and returned in the client manifest as load for OOB load() round-trip.
from typing import Annotated
from pyjinhx import MutationKey, PjxKey, ReactiveComponent
class Keys(MutationKey):
TODOS = "todos"
class ItemRow(ReactiveComponent, react={Keys.TODOS}):
todo_id: Annotated[int, PjxKey()]
@classmethod
def load(cls, todo_id: int | str) -> "ItemRow":
# The cache wrapper passes the key as a string; convert before use.
resolved_id = int(todo_id)
...
mutates¶
Decorator for store mutation methods. Each arg must be a MutationKey member — bare strings raise TypeError at decoration time. After the wrapped function returns, invalidates the load cache and accumulates pending dirtied keys for the next reactive render().
from pyjinhx import MutationKey, mutates
class Keys(MutationKey):
TODOS = "todos"
class Store:
@mutates(Keys.TODOS)
def add(self, text: str) -> None:
...
PjxContext¶
Opaque base for request-scoped data available inside reactive load() and any component method that declares a PjxContext parameter. Subclass with your own frozen dataclass fields (database session, user id, feature flags).
PjxContext.current / PjxContext.bind¶
Return or set the load context for the current scope. Any component method that declares a parameter annotated with PjxContext (or a subclass) — including reactive load() — receives the current context when the parameter is left unbound; an explicitly passed argument takes precedence. At most one such parameter is allowed per method.
Prefer Registry.request_scope(load_context=ctx) in web apps — it combines registry isolation, request cache, mutation tracking, and load context in one call.
from pyjinhx import PjxContext, Registry
from pyjinhx.integrations.fastapi import FastAPIClientBackend
@dataclass(frozen=True)
class AppLoadContext(PjxContext):
db: Session
with Registry.request_scope(
load_context=AppLoadContext(db=session),
client_backend=FastAPIClientBackend(request),
):
html = TodoList.render()
Reactive dev¶
Development-time guardrails for catching common reactive mistakes.
enable_reactive_dev¶
Enable guardrails. When enabled:
- Warns if
@mutatesrecorded dirtied keys but no reactiverender()consumed them in the request scope. - Warns if mutations are pending but no
ClientBackendis active (OOB swaps skipped). - Validates that
depends_on()is a subset of the staticreactsuperset on eachload().
Set strict=True to raise RuntimeError instead of logging warnings.
disable_reactive_dev¶
Disable all dev guardrails.
dependency_graph¶
Map each declared reactive key to the component class names that depend on it.
format_dependency_graph¶
Format the dependency graph as a text table or Mermaid flowchart. Useful for debugging and documentation.