Specifications#
Describes various data structures and protocols used to define and communicate virtual document object models (VDOM). The definitions below follow in the footsteps of a specification created by Nteract and which was built into JupyterLab. While ReactPy’s specification for VDOM is fairly well established, it should not be relied until it’s been fully adopted by the aforementioned organizations.
VDOM#
A set of definitions that explain how ReactPy creates a virtual representation of the document object model. We’ll begin by looking at a bit of HTML that we’ll convert into its VDOM representation:
<div>
Put your name here:
<input
type="text"
minlength="4"
maxlength="8"
onchange="a_python_callback(event)"
/>
</div>
Note
For context, the following Python code would generate the HTML above:
import reactpy
async def a_python_callback(new):
...
name_input_view = reactpy.html.div(
reactpy.html.input(
{
"type": "text",
"minLength": 4,
"maxLength": 8,
"onChange": a_python_callback,
}
),
["Put your name here: "],
)
We’ll take this step by step in order to show exactly where each piece of the VDOM
model comes from. To get started we’ll convert the outer <div/>
:
{
"tagName": "div",
"children": [
"To perform an action",
...
],
"attributes": {},
"eventHandlers": {}
}
Note
As we move though our conversation we’ll be using ...
to fill in places that we
haven’t converted yet.
In this simple case, all we’ve done is take the name of the HTML element (div
in
this case) and inserted it into the tagName
field of a dictionary. Then we’ve taken
the inner HTML and added to a list of children where the text "to perform an action"
has been made into a string, and the inner input
(yet to be converted) will be
expanded out into its own VDOM representation. Since the outer div
is pretty simple
there aren’t any attributes
or eventHandlers
.
No we come to the inner input
. If we expand this out now we’ll get the following:
{
"tagName": "div",
"children": [
"To perform an action",
{
"tagName": "input",
"children": [],
"attributes": {
"type": "text",
"minLength": 4,
"maxLength": 8
},
"eventHandlers": ...
}
],
"attributes": {},
"eventHandlers": {}
}
Here we’ve had to add some attributes to our VDOM. Take note of the differing capitalization - instead of using all lowercase (an HTML convention) we’ve used camelCase which is very common in JavaScript.
Last, but not least we come to the eventHandlers
for the input
:
{
"tagName": "div",
"children": [
"To perform an action",
{
"tagName": "input",
"children": [],
"attributes": {
"type": "text",
"minLength": 4,
"maxLength": 8
},
"eventHandlers": {
"onChange": {
"target": "unique-id-of-a_python_callback",
"preventDefault": False,
"stopPropagation": False
}
}
}
],
"attributes": {},
"eventHandlers": {}
}
Again we’ve changed the all lowercase onchange
into a cameCase onChange
event
type name. The various properties for the onChange
handler are:
target
: the unique ID for a Python callback that exists in the backend.preventDefault
: Stop the event’s default action. More info here.stopPropagation
: prevent the event from bubbling up through the DOM. More info here.
VDOM JSON Schema#
To clearly describe the VDOM spec we’ve created a JSON Schema:
{
"$ref": "#/definitions/element",
"$schema": "http://json-schema.org/draft-07/schema",
"definitions": {
"element": {
"dependentSchemas": {
"error": {
"properties": {
"tagName": {
"maxLength": 0
}
}
}
},
"properties": {
"attributes": {
"type": "object"
},
"children": {
"$ref": "#/definitions/elementChildren"
},
"error": {
"type": "string"
},
"eventHandlers": {
"$ref": "#/definitions/elementEventHandlers"
},
"importSource": {
"$ref": "#/definitions/importSource"
},
"key": {
"type": "string"
},
"tagName": {
"type": "string"
}
},
"required": ["tagName"],
"type": "object"
},
"elementChildren": {
"items": {
"$ref": "#/definitions/elementOrString"
},
"type": "array"
},
"elementEventHandlers": {
"patternProperties": {
".*": {
"$ref": "#/definitions/eventHander"
}
},
"type": "object"
},
"elementOrString": {
"if": {
"type": "object"
},
"then": {
"$ref": "#/definitions/element"
},
"type": ["object", "string"]
},
"eventHandler": {
"properties": {
"preventDefault": {
"type": "boolean"
},
"stopPropagation": {
"type": "boolean"
},
"target": {
"type": "string"
}
},
"required": ["target"],
"type": "object"
},
"importSource": {
"properties": {
"fallback": {
"if": {
"not": {
"type": "null"
}
},
"then": {
"$ref": "#/definitions/elementOrString"
},
"type": ["object", "string", "null"]
},
"source": {
"type": "string"
},
"sourceType": {
"enum": ["URL", "NAME"]
},
"unmountBeforeUpdate": {
"type": "boolean"
}
},
"required": ["source"],
"type": "object"
}
}
}
JSON Patch#
Updates to VDOM modules are sent using the JSON Patch specification.
… this section is still Under construction 🚧