kopular 0.11.2 → 0.11.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LLM.md +8 -0
- package/README.md +2 -0
- package/package.json +1 -1
- package/src/component.js +8 -0
- package/src/component.ks +25 -0
package/LLM.md
CHANGED
|
@@ -128,6 +128,14 @@ w.Bump(); // re-render
|
|
|
128
128
|
reconciliation. Compose independent components into stable slots (see `Router`'s own
|
|
129
129
|
pattern of keeping page instances alive) to avoid this rather than nesting components
|
|
130
130
|
that both re-render.
|
|
131
|
+
- **`Update()` before `Mount()` is a safe no-op**, not an error. This matters for two
|
|
132
|
+
sibling `Component`s (e.g. two `Router` pages) that share one injected service's
|
|
133
|
+
`state<T>` and both `Subscribe()` it — every route's page is constructed eagerly (see
|
|
134
|
+
`Router.AddRoute`), so at any given time most of them are constructed but never
|
|
135
|
+
`Mount()`ed. Changing that shared state fires `Subscribe` on all of them, including the
|
|
136
|
+
ones that aren't the currently-routed page — `Update()` just skips the render/replace
|
|
137
|
+
for those, since `Mount()` will run a fresh `Render()` anyway whenever one of them
|
|
138
|
+
actually becomes routed.
|
|
131
139
|
- `RenderError(string message)`: `virtual`, called by `Mount()`/`Update()` (via a private
|
|
132
140
|
`SafeRender()` wrapper) when `Render()` throws, instead of letting the exception
|
|
133
141
|
propagate uncaught and crash whatever triggered the render (a click handler, a `Router`
|
package/README.md
CHANGED
|
@@ -543,3 +543,5 @@ mounted children, those children aren't automatically re-mounted into the parent
|
|
|
543
543
|
tree (real reconciliation, the way React/Vue handle this, is real vdom-diffing work well
|
|
544
544
|
beyond v1). Compose independent components into stable slots (see `Router`'s own pattern
|
|
545
545
|
of keeping page instances alive rather than rebuilding them) to avoid the issue.
|
|
546
|
+
`Update()` called before `Mount()` is a safe no-op — see `LLM.md`'s `Component` section
|
|
547
|
+
for exactly when that happens (sibling pages sharing one injected service's `state<T>`).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kopular",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.4",
|
|
4
4
|
"description": "Kopular: a small component framework for KopScript — components, reactive state, constructor-injected services, routing, real compiled templates, and HTTP, with no DI container",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
package/src/component.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { Event, Element, Document, Location, History, Window, document, location, history, window } from "./dom.js";
|
|
2
2
|
|
|
3
3
|
export class Component {
|
|
4
|
+
constructor() {
|
|
5
|
+
this.IsMounted = false;
|
|
6
|
+
}
|
|
7
|
+
|
|
4
8
|
Render() {
|
|
5
9
|
return document.createElement("div");
|
|
6
10
|
}
|
|
@@ -21,9 +25,13 @@ export class Component {
|
|
|
21
25
|
this.ParentElement = parent;
|
|
22
26
|
this.Root = this.SafeRender();
|
|
23
27
|
parent.appendChild(this.Root);
|
|
28
|
+
this.IsMounted = true;
|
|
24
29
|
}
|
|
25
30
|
|
|
26
31
|
Update() {
|
|
32
|
+
if (!this.IsMounted) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
27
35
|
let newRoot = this.SafeRender();
|
|
28
36
|
this.ParentElement.replaceChild(newRoot, this.Root);
|
|
29
37
|
this.Root = newRoot;
|
package/src/component.ks
CHANGED
|
@@ -16,6 +16,19 @@ using "./dom";
|
|
|
16
16
|
class Component {
|
|
17
17
|
protected Element Root;
|
|
18
18
|
private Element ParentElement;
|
|
19
|
+
// Set true only once Mount() actually runs. A page Component is commonly
|
|
20
|
+
// constructed eagerly (e.g. Router.AddRoute takes an already-built
|
|
21
|
+
// instance — see Router's own header comment) long before it's ever
|
|
22
|
+
// Mount()ed, and two sibling pages sharing one injected service's
|
|
23
|
+
// state<T> (Pure DI — both Subscribe() the same field) both get notified
|
|
24
|
+
// on any change regardless of which one is actually the currently-routed,
|
|
25
|
+
// mounted page. Update() guards on this so that notification is a safe
|
|
26
|
+
// no-op for the unmounted one, instead of a crash (see Update() below).
|
|
27
|
+
private bool IsMounted;
|
|
28
|
+
|
|
29
|
+
constructor() {
|
|
30
|
+
this.IsMounted = false;
|
|
31
|
+
}
|
|
19
32
|
|
|
20
33
|
public virtual Element Render() {
|
|
21
34
|
return document.createElement("div");
|
|
@@ -45,9 +58,21 @@ class Component {
|
|
|
45
58
|
this.ParentElement = parent;
|
|
46
59
|
this.Root = this.SafeRender();
|
|
47
60
|
parent.appendChild(this.Root);
|
|
61
|
+
this.IsMounted = true;
|
|
48
62
|
}
|
|
49
63
|
|
|
64
|
+
// A no-op, not an error, when called before Mount() — see the class-level
|
|
65
|
+
// comment on IsMounted for exactly when this happens for real (a
|
|
66
|
+
// shared-service state change reaching a sibling page that isn't the one
|
|
67
|
+
// currently routed/mounted). Nothing is lost: SafeRender() would only be
|
|
68
|
+
// thrown away unread since there's no live DOM parent to put it in, and
|
|
69
|
+
// Mount() itself always calls SafeRender() fresh whenever this component
|
|
70
|
+
// does become the routed page, picking up whatever the current state is
|
|
71
|
+
// at that point.
|
|
50
72
|
protected void Update() {
|
|
73
|
+
if (!this.IsMounted) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
51
76
|
Element newRoot = this.SafeRender();
|
|
52
77
|
this.ParentElement.replaceChild(newRoot, this.Root);
|
|
53
78
|
this.Root = newRoot;
|