neo.mjs 10.0.0-beta.6 → 10.0.0

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.
Files changed (51) hide show
  1. package/.github/RELEASE_NOTES/v10.0.0-beta.1.md +20 -0
  2. package/.github/RELEASE_NOTES/v10.0.0-beta.2.md +73 -0
  3. package/.github/RELEASE_NOTES/v10.0.0-beta.3.md +39 -0
  4. package/.github/RELEASE_NOTES/v10.0.0.md +52 -0
  5. package/ServiceWorker.mjs +2 -2
  6. package/apps/portal/index.html +1 -1
  7. package/apps/portal/view/ViewportController.mjs +6 -4
  8. package/apps/portal/view/examples/List.mjs +28 -19
  9. package/apps/portal/view/home/FooterContainer.mjs +1 -1
  10. package/examples/functional/button/base/MainContainer.mjs +207 -0
  11. package/examples/functional/button/base/app.mjs +6 -0
  12. package/examples/functional/button/base/index.html +11 -0
  13. package/examples/functional/button/base/neo-config.json +6 -0
  14. package/learn/blog/v10-deep-dive-functional-components.md +293 -0
  15. package/learn/blog/v10-deep-dive-reactivity.md +522 -0
  16. package/learn/blog/v10-deep-dive-state-provider.md +432 -0
  17. package/learn/blog/v10-deep-dive-vdom-revolution.md +194 -0
  18. package/learn/blog/v10-post1-love-story.md +383 -0
  19. package/learn/guides/uibuildingblocks/WorkingWithVDom.md +26 -2
  20. package/package.json +3 -3
  21. package/src/DefaultConfig.mjs +2 -2
  22. package/src/Neo.mjs +47 -45
  23. package/src/component/Abstract.mjs +412 -0
  24. package/src/component/Base.mjs +18 -380
  25. package/src/core/Base.mjs +34 -33
  26. package/src/core/Effect.mjs +30 -34
  27. package/src/core/EffectManager.mjs +101 -14
  28. package/src/core/Observable.mjs +69 -65
  29. package/src/form/field/Text.mjs +11 -5
  30. package/src/functional/button/Base.mjs +384 -0
  31. package/src/functional/component/Base.mjs +51 -145
  32. package/src/layout/Cube.mjs +8 -4
  33. package/src/manager/VDomUpdate.mjs +179 -94
  34. package/src/mixin/VdomLifecycle.mjs +4 -1
  35. package/src/state/Provider.mjs +41 -27
  36. package/src/util/VDom.mjs +11 -4
  37. package/src/util/vdom/TreeBuilder.mjs +38 -62
  38. package/src/worker/mixin/RemoteMethodAccess.mjs +1 -6
  39. package/test/siesta/siesta.js +15 -3
  40. package/test/siesta/tests/VdomCalendar.mjs +7 -7
  41. package/test/siesta/tests/VdomHelper.mjs +7 -7
  42. package/test/siesta/tests/classic/Button.mjs +113 -0
  43. package/test/siesta/tests/core/EffectBatching.mjs +46 -41
  44. package/test/siesta/tests/functional/Button.mjs +113 -0
  45. package/test/siesta/tests/state/ProviderNestedDataConfigs.mjs +59 -0
  46. package/test/siesta/tests/vdom/Advanced.mjs +14 -8
  47. package/test/siesta/tests/vdom/VdomAsymmetricUpdates.mjs +9 -9
  48. package/test/siesta/tests/vdom/VdomRealWorldUpdates.mjs +6 -6
  49. package/test/siesta/tests/vdom/layout/Cube.mjs +11 -7
  50. package/test/siesta/tests/vdom/table/Container.mjs +9 -5
  51. package/src/core/EffectBatchManager.mjs +0 -67
@@ -0,0 +1,293 @@
1
+ # Beyond Hooks: A New Breed of Functional Components for a Multi-Threaded World
2
+
3
+ If you're a seasoned React developer, you've mastered the art of hooks. You know how to build complex, stateful UIs with
4
+ `useState`, `useEffect`, and `useMemo`. You also know the trade-offs—the intricate dependency arrays, the constant battle
5
+ against unnecessary re-renders, and the "memoization tax" you pay to keep your application performant.
6
+
7
+ We've been taught that these trade-offs are fundamental to the functional component model. But what if they aren't?
8
+ What if they are merely symptoms of a single-threaded architecture?
9
+
10
+ This article will show you a new breed of functional component, born from a multi-threaded world, that eliminates these
11
+ compromises by design. We didn't build them to copy other frameworks; we built them because our architecture unlocked a
12
+ better way to write UIs.
13
+
14
+ *(Part 3 of 5 in the v10 blog series. Details at the bottom.)*
15
+
16
+ ### A First Look: The Anatomy of a Neo.mjs Functional Component
17
+
18
+ Before we dive into the "why," let's look at the "what." Here is a simple, complete, and reactive functional component
19
+ in Neo.mjs. Keep this structure in mind as we explore how it solves the problems you've come to accept as normal.
20
+
21
+ ```javascript
22
+ import {defineComponent, useConfig, useEvent} from 'neo.mjs';
23
+
24
+ export default defineComponent({
25
+ // The component's public API (like props)
26
+ config: {
27
+ className: 'My.Counter',
28
+ labelText_: 'Counter:'
29
+ },
30
+
31
+ // The function that creates the VDOM
32
+ createVdom(config) {
33
+ // Internal, private state (like useState)
34
+ const [count, setCount] = useConfig(0);
35
+
36
+ // An event listener that updates the private state
37
+ useEvent('click', () => setCount(prev => prev + 1));
38
+
39
+ return {
40
+ tag: 'button',
41
+ cn: [
42
+ {tag: 'span', text: config.labelText},
43
+ {tag: 'span', cls: ['count-badge'], text: count}
44
+ ]
45
+ }
46
+ }
47
+ });
48
+ ```
49
+
50
+ Notice what's missing: there are no dependency arrays, no `memo` wrappers, and no `useCallback` hooks. Now, let's explore
51
+ the architecture that makes this clean, simple code possible.
52
+
53
+ ---
54
+
55
+ ### The Architectural Divide: Why Your Component's Environment Matters
56
+
57
+ Before we deconstruct the problems, we have to address the fundamental difference that changes everything. In a
58
+ traditional framework like React, your component function, its state, its reconciliation (diffing), and its DOM
59
+ manipulation all happen on the **same main thread** that is responsible for user interactions.
60
+
61
+ In Neo.mjs, the architecture is fundamentally different:
62
+
63
+ 1. **Your Application Logic (including your component's `createVdom` function) runs in a dedicated App Worker.**
64
+ 2. **The VDOM diffing happens in a separate VDom Worker.**
65
+ 3. **The Main Thread is left with one primary job: applying the calculated DOM patches.**
66
+
67
+ This isn't just a minor difference; it's a paradigm shift. Your component code is decoupled from the rendering engine,
68
+ which allows for a level of performance and predictability that is architecturally impossible on the main thread.
69
+ With this in mind, let's see how this new architecture solves old problems.
70
+
71
+ ---
72
+
73
+ ### Deconstructing the "React Tax": How a New Architecture Solves Old Problems
74
+
75
+ Let's tackle the compromises you've learned to live with, one by one, and show how a multi-threaded architecture solves
76
+ them at their root.
77
+
78
+ #### 1. The Problem: Cascading Re-Renders & The `memo` Tax
79
+
80
+ **The React Way:** You know the drill. A state change in a parent component triggers a re-render. By default, React then
81
+ re-renders **all of its children**, whether their props have changed or not. To prevent this performance drain,
82
+ you are forced to pay the `memo` tax: wrapping components in `React.memo()`, manually memoizing functions with
83
+ `useCallback()`, and objects with `useMemo()`. This manual optimization becomes a core, and often frustrating,
84
+ part of the development process.
85
+
86
+ ```javascript
87
+ // A typical "optimized" React component
88
+ const MyComponent = React.memo(({ onButtonClick, user }) => {
89
+ console.log('Rendering MyComponent');
90
+ return <button onClick={onButtonClick}>{user.name}</button>;
91
+ });
92
+
93
+ const App = () => {
94
+ const [count, setCount] = useState(0);
95
+
96
+ // We must wrap this in useCallback to prevent MyComponent from re-rendering
97
+ // every time the App component's state changes.
98
+ const handleClick = useCallback(() => {
99
+ console.log('Button clicked!');
100
+ }, []);
101
+
102
+ // We must wrap this in useMemo to ensure the object reference is stable.
103
+ const user = useMemo(() => ({ name: 'John Doe' }), []);
104
+
105
+ return (
106
+ <div>
107
+ <button onClick={() => setCount(c => c + 1)}>App Clicks: {count}</button>
108
+ <MyComponent onButtonClick={handleClick} user={user} />
109
+ </div>
110
+ );
111
+ };
112
+ ```
113
+
114
+ **The Neo.mjs Solution: Surgical Effects, Not Brute-Force Renders**
115
+
116
+ Our `createVdom` method is a surgical `Effect`. It automatically and precisely tracks every piece of reactive state it reads.
117
+ When a piece of state changes, **only the specific `createVdom` effects that depend on that exact piece of state are queued
118
+ for re-execution.**
119
+
120
+ There are no cascading re-renders. If a parent's `createVdom` re-runs, but the configs passed to a child have not changed,
121
+ the child component's `createVdom` function is **never executed**.
122
+
123
+ This means `memo`, `useCallback`, and `useMemo` are not needed. The architecture is efficient by default, eliminating an
124
+ entire class of performance optimizations and bugs.
125
+
126
+ #### 2. The Problem: The Boilerplate of Immutability
127
+
128
+ **The React Way:** To change a nested object in React state, you have to meticulously reconstruct the object path with
129
+ spread syntax (`...`), creating new references for every level. This is required to signal to React's diffing algorithm
130
+ that something has changed.
131
+
132
+ ```javascript
133
+ // The familiar immutable update dance
134
+ setState(prevState => ({
135
+ ...prevState,
136
+ deeply: {
137
+ ...prevState.deeply,
138
+ nested: {
139
+ ...prevState.deeply.nested,
140
+ property: 'new value'
141
+ }
142
+ }
143
+ }));
144
+ ```
145
+
146
+ **The Neo.mjs Solution: Mutability for You, Immutability for the Machine**
147
+
148
+ We believe the developer should not carry this cognitive load. In Neo.mjs, you can just mutate the state directly.
149
+ It's simple and intuitive.
150
+
151
+ ```javascript
152
+ // Just change the value. That's it.
153
+ this.myObject.deeply.nested.property = 'new value';
154
+ ```
155
+
156
+ How does it work? When an update is triggered, the framework handles creating an immutable JSON snapshot of the VDOM for
157
+ the diffing process in the VDom Worker. We provide the best of both worlds: simple, direct mutation for the developer
158
+ and a safe, immutable structure for the high-performance diffing algorithm.
159
+
160
+ #### 3. The Problem: The "SSR and Hydration is the ONLY way" Mindset
161
+
162
+ **The React/Next.js Way:** The industry has invested heavily in Server-Side Rendering and hydration to improve perceived
163
+ performance and SEO. For content-heavy sites, this is a valid strategy. But for complex, stateful Single-Page Applications,
164
+ it introduces immense complexity: the "hydration crisis," the difficulty of managing server vs. client components, and
165
+ the fact that after all that work, your application is *still* running on the client's main thread.
166
+
167
+ **The Neo.mjs Solution: Blueprints, Not Dehydrated HTML**
168
+
169
+ We offer a more robust and scalable model for SPAs. Instead of sending pre-rendered HTML that needs to be brought back
170
+ to life, we can send a compact **JSON blueprint**. The client-side engine, running in a worker, constructs the live,
171
+ interactive UI from this blueprint. This is a more efficient, more predictable, and architecturally cleaner way to build
172
+ complex applications that are not primarily focused on static content.
173
+
174
+ ---
175
+
176
+ ### The "WOW" Effect: Building a Real Application
177
+
178
+ The simple counter example is a great start, but the true power of functional components is revealed when you build a
179
+ complete, interactive application. Let's build a slightly more advanced "Task List" application to demonstrate how all
180
+ the pieces come together.
181
+
182
+ This example will showcase:
183
+ - **Component Composition:** Using a class-based `List` component within our functional view.
184
+ - **State Management:** Tracking the currently selected task.
185
+ - **Conditional Rendering:** Displaying task details only when a task is selected.
186
+ - **Event Handling:** Updating state based on events from a child component.
187
+
188
+ Here is the complete, interactive example.
189
+
190
+ ```javascript live-preview
191
+ import {defineComponent, useConfig} from 'neo.mjs';
192
+ import List from 'neo.mjs/src/list/Base.mjs';
193
+ import Store from 'neo.mjs/src/data/Store.mjs';
194
+
195
+ // 1. Define a simple data Store for our list
196
+ const TaskStore = Neo.create(Store, {
197
+ data: [
198
+ {id: 1, title: 'Build a better component model', description: 'Re-imagine what a component can be when freed from the main thread.'},
199
+ {id: 2, title: 'Create a new reactivity system', description: 'Design a two-tier system that is both powerful and intuitive.'},
200
+ {id: 3, title: 'Revolutionize the VDOM', description: 'Build an asymmetric, off-thread VDOM engine for maximum performance.'}
201
+ ]
202
+ });
203
+
204
+ // 2. Define our main application view
205
+ export default defineComponent({
206
+ config: {
207
+ className: 'My.TaskListApp',
208
+ layout: {ntype: 'hbox', align: 'stretch'}
209
+ },
210
+ createVdom() {
211
+ // 3. Manage the selected task with useConfig
212
+ const [selectedTask, setSelectedTask] = useConfig(null);
213
+
214
+ const onSelectionChange = ({value}) => {
215
+ // 4. Update our state when the list selection changes
216
+ setSelectedTask(TaskStore.get(value[0]));
217
+ };
218
+
219
+ const paneStyle = {
220
+ border : '1px solid #c0c0c0',
221
+ margin : '10px',
222
+ padding: '10px'
223
+ };
224
+
225
+ return {
226
+ cn: [{
227
+ // 5. The List Component
228
+ module : List,
229
+ store : TaskStore,
230
+ width : 250,
231
+ style : paneStyle,
232
+ listeners: {
233
+ selectionChange: onSelectionChange
234
+ }
235
+ }, {
236
+ // 6. The Details Pane (with conditional rendering)
237
+ flex: 1,
238
+ style: paneStyle,
239
+ cn: selectedTask ? [
240
+ {tag: 'h2', text: selectedTask.title},
241
+ {tag: 'p', text: selectedTask.description}
242
+ ] : [{
243
+ tag : 'div',
244
+ style: {color: '#888'},
245
+ text : 'Please select a task to see the details.'
246
+ }]
247
+ }]
248
+ }
249
+ }
250
+ });
251
+ ```
252
+
253
+ This single `defineComponent` call creates a fully-featured application. Notice how the `createVdom` function is a pure,
254
+ declarative representation of the UI. When the `selectedTask` state changes, the framework surgically re-renders only
255
+ the details pane. This is the power of the new component model in action: complex, stateful, and performant applications
256
+ with beautifully simple code.
257
+
258
+ ---
259
+
260
+ ### The AI Connection: The Inevitable Next Step
261
+
262
+ This blueprint-first, surgically reactive, and mutable-by-default model isn't just better for you; it's the architecture
263
+ an AI would choose. An AI can easily generate and manipulate a structured JSON blueprint, but it struggles to generate
264
+ flawless, complex JSX. By building on these principles, you are not just using a new framework; you are future-proofing
265
+ your skills for the AI era.
266
+
267
+ ---
268
+
269
+ ### Conclusion: A Different Philosophy, A Better Component
270
+
271
+ Neo.mjs functional components are not a "React clone." They are a re-imagining of what a functional component can be
272
+ when freed from the architectural constraints of the main thread. They offer a development experience that is not only
273
+ more performant by default but also simpler, more intuitive, and ready for the AI-driven future of the web.
274
+
275
+ This is what it feels like to stop paying the performance tax and start building again.
276
+
277
+ The clean, hook-based API for functional components is possible because it stands on the shoulders of a robust, modular,
278
+ and deeply integrated class system. We've engineered the framework's core to handle the complex machinery of reactivity
279
+ and lifecycle management automatically. To learn more about the powerful engine that makes this all possible, see our
280
+ deep dive on the **Two-Tier Reactivity System**.
281
+
282
+ Next, we will look at how this architecture revolutionizes the very way we render UIs with the Asymmetric VDOM
283
+ and JSON Blueprints.
284
+
285
+ ---
286
+
287
+ ## The Neo.mjs v10 Blog Post Series
288
+
289
+ 1. [A Frontend Love Story: Why the Strategies of Today Won't Build the Apps of Tomorrow](./v10-post1-love-story.md)
290
+ 2. [Deep Dive: Named vs. Anonymous State - A New Era of Component Reactivity](./v10-deep-dive-reactivity.md)
291
+ 3. Beyond Hooks: A New Breed of Functional Components for a Multi-Threaded World
292
+ 4. [Deep Dive: The VDOM Revolution - JSON Blueprints & Asymmetric Rendering](./v10-deep-dive-vdom-revolution.md)
293
+ 5. [Deep Dive: The State Provider Revolution](./v10-deep-dive-state-provider.md)