Async data and Suspense¶
Create a stable resource outside render. Its loader can be an async Python
function. read(key) returns the resolved value or suspends the component.
import asyncio
from pyreact_forge import component, error_boundary, html, resource, suspense
async def load_message(key):
await asyncio.sleep(0.2)
return {"message": f"Loaded {key}"}
messages = resource(load_message)
@component
def Message():
data = messages.read("welcome")
return html.p(data["message"])
@component
def App():
return error_boundary(
suspense(Message(), fallback=html.p("Loading…")),
fallback=html.p("Could not load the message.", role="alert"),
)
Caching and retry¶
preload(key) starts a load and returns a stable promise holder. read(key)
uses that same promise; creating a new resource inside every render can restart
work indefinitely. Keys must be hashable. invalidate(key) removes one cached
entry, and clear() removes all entries. Neither schedules a render by itself.
For a retry button, invalidate the failing key, update component state and change
the error boundary's reset_key so its fallback is cleared. Treat error recovery
as an explicit UI state.
Fetching a real endpoint¶
Pyodide offers pyodide.http.pyfetch. Call it from the resource loader, check
the response status and await response.json(). Browser CORS and authentication
rules still apply. Do not embed private API keys in browser Python.
Transitions and optimistic changes¶
Use use_transition / start_transition for non-urgent work. For an optimistic
action, make its initial optimistic update synchronously before returning an
awaitable. Wrap state updates after an await in start_transition where React
requires it. The hooks reference explains this Python
async boundary, and the conformance example demonstrates the tested pattern.