Simple Counter App
Source Code
packages = ["reactpy", "ssl", "jsonpointer"]
[splashscreen]
enabled = false
import js
import asyncio
from reactpy import html, component, use_state
from reactpy.core.layout import Layout
from jsonpointer import set_pointer
from pyodide.ffi.wrappers import add_event_listener
# --- Application Code ---------------------------------------------------------
@component
def app():
value, set_value = use_state(0)
return html.article(
html.div(
{"class": "grid"},
html.button({"on_click": lambda event: set_value(value + 1)}, "+"),
html.button({"on_click": lambda event: set_value(value - 1)}, "-"),
),
"Current value",
html.pre({"style": {"font-style": "bold"}}, str(value)),
)
# --- Framework Code ---------------------------------------------------------
model = {}
def apply_update(update):
if update["path"]:
set_pointer(model, update["path"], update["model"])
else:
model.update(update["model"])
def render_model(layout, model):
root = js.document.getElementById("root")
root.innerHTML = ""
_render_model(layout, root, model)
def _render_model(layout, parent, model):
if isinstance(model, str):
parent.appendChild(js.document.createTextNode(model))
elif isinstance(model, dict):
if not model["tagName"]:
for child in model.get("children", []):
_render_model(layout, parent, child)
return
tag = model["tagName"]
attributes = model.get("attributes", {})
children = model.get("children", [])
element = js.document.createElement(tag)
for key, value in attributes.items():
if key == "style":
for style_key, style_value in value.items():
setattr(element.style, style_key, style_value)
else:
element.setAttribute(key, value)
for event_name, event_handler_model in model.get("eventHandlers", {}).items():
_create_event_handler(layout, element, event_name, event_handler_model)
for child in children:
_render_model(layout, element, child)
parent.appendChild(element)
else:
raise ValueError(f"Unknown model type: {type(model)}")
def _create_event_handler(layout, element, event_name, event_handler_model):
target = event_handler_model["target"]
def event_handler(*args):
asyncio.create_task(layout.deliver({
"type": "layout-event",
"target": target,
"data": args,
}))
event_name = event_name.lstrip("on_").lower().replace("_", "")
add_event_listener(element, event_name, event_handler)
async def main():
async with Layout(app()) as layout:
while True:
update = await layout.render()
apply_update(update)
render_model(layout, model)
asyncio.create_task(main())