Skip to content

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.