Skip to content

Commit d0026fb

Browse files
committed
docs(learn): event-handler detection is by callable(), not the key pattern
- responding-to-events: separate_attributes_handlers_and_inline_javascript tests callable(v) before ever reading the key, so on_click/foo are handlers too; EVENT_ATTRIBUTE_PATTERN only routes *strings* to inlineJavaScript. A None handler is an inert attribute, not a serialized handler. - creating-html-tags: createElementNS lives in the bundled preact chunk, not @reactpy/client source (grep finds nothing there); stop citing a hash-named asset and a minified Q symbol nobody can look up.
1 parent 3f0ce61 commit d0026fb

2 files changed

Lines changed: 4 additions & 4 deletions

File tree

‎docs/src/learn/creating-html-tags.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ from reactpy import Vdom
201201
Vdom("text")("hello") # {'tagName': 'text', 'children': ['hello']}
202202
```
203203

204-
The bundled client creates every element with `#!python document.createElementNS(namespace, ...)` and picks the namespace from the tag name — `static/index-bz53xswt.js` sets `#!python Q = "http://www.w3.org/2000/svg"` when the type is `#!python "svg"` and the MathML URI for `#!python "math"`. Inside the SVG namespace attribute names are forwarded verbatim rather than lower-cased, so `#!python viewBox` keeps its capital `#!python B` — write it that way, since `#!python viewbox` is a different attribute to the DOM.
204+
The bundled client creates every element through `#!js document.createElementNS(namespace, ...)`, picking the namespace from the tag name: `#!python "svg"` gets `http://www.w3.org/2000/svg`, `#!python "math"` gets the MathML URI, everything else the XHTML one. That logic lives in the preact runtime ReactPy bundles into its client chunks, not in ReactPy's own `#!js @reactpy/client` source — so grep there for `createElementNS` and you will find nothing. Inside the SVG namespace attribute names are forwarded verbatim rather than lower-cased, so `#!python viewBox` keeps its capital `#!python B` — write it that way, since `#!python viewbox` is a different attribute to the DOM.
205205

206206
## Choosing children over attributes
207207

‎docs/src/learn/responding-to-events.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,9 @@ By convention, it is common to name event handlers as `#!python handle` followed
6868
html.button({"onClick": handle_click}, "Click me") # correct
6969
```
7070

71-
Writing `#!python html.button({"on_click": handle_click}, ...)` does **not** raise. ReactPy accepts any `#!python on*` key as a handler and ships it to the browser verbatim — but no browser event is named `#!python on_click`, so the button looks fine and simply never fires. Only the names *you* define (variables, components, component props) are `#!python snake_case`.
71+
Writing `#!python html.button({"on_click": handle_click}, ...)` does **not** raise. ReactPy ships it to the browser verbatim as a handler for an event called `#!python _click` — the client registers a listener for the key with the leading `#!js on` stripped — and no browser ever fires one. The button looks fine and simply never responds. Only the names *you* define (variables, components, component props) are `#!python snake_case`.
7272

73-
ReactPy recognises a key as an event handler when it matches `#!python ^on[A-Z]\w+` (`core/vdom.py`, `EVENT_ATTRIBUTE_PATTERN`).
73+
Note what does and does not decide "this is a handler". In `core/vdom.py`, `separate_attributes_handlers_and_inline_javascript` the test is **`callable(v)` on the value**, checked before the key is ever looked at: a callable under `#!python "on_click"`, or even under `#!python "foo"`, lands in `#!python eventHandlers`. The key pattern `#!python ^on[A-Z]\w+` (`core/vdom.py`, `EVENT_ATTRIBUTE_PATTERN`) is used only for the *string* case below — it decides whether a string becomes `#!python inlineJavaScript` or a plain attribute. So `#!python {"onClick": "alert(1)"}` runs as JavaScript, while `#!python {"onclick": "alert(1)"}` and `#!python {"on_click": "alert(1)"}` become inert attributes.
7474

7575
Alternatively, you can define an event handler inline with a `#!python lambda`:
7676

@@ -102,7 +102,7 @@ Inline event handlers are convenient for short functions.
102102
html.button({"onClick": handle_click()}, "...") # wrong: fires during render
103103
```
104104

105-
In the second case the `#!python ()` calls `#!python handle_click` _immediately_ during [rendering](./render-and-commit.md), without any clicks — because the Python inside the call is evaluated right away, exactly like an expression inside JSX braces. Whatever it returns is then stored as the handler, which is usually `#!python None`. ReactPy does not reject that: `#!python None` is serialised as a handler that never runs, so the button renders and does nothing, forever.
105+
In the second case the `#!python ()` calls `#!python handle_click` _immediately_ during [rendering](./render-and-commit.md), without any clicks — because the Python inside the call is evaluated right away, exactly like an expression inside JSX braces. Whatever it returns is what gets stored, and a function that ends without a `#!python return` gives `#!python None`. ReactPy does not reject that: handlers are picked out by `#!python callable(v)`, and `#!python None` is not callable, so it falls through to the plain attributes and ships as `#!python {"onClick": None}` — a DOM attribute named `onClick` holding `null`. Nothing registers, nothing fires, and the button renders and does nothing, forever.
106106

107107
The same trap appears with inline code:
108108

0 commit comments

Comments
 (0)