1- # Act I Deep Dive: The State Provider Revolution
1+ # Deep Dive: The State Provider Revolution
22
33** Subtitle: How Neo.mjs Delivers Intuitive State Management Without the Performance Tax**
44
@@ -41,62 +41,202 @@ This new foundation is what makes the modern `state.Provider` so powerful. It do
4141it * knows* them. When a binding function runs, ` core.Effect ` observes every reactive property you access—no matter where
4242it lives—and builds a precise dependency graph in real-time.
4343
44- The result is an API that is not only more powerful but also simpler and more intuitive.
44+ The result is an API that is not only more powerful but also simpler and more intuitive, especially when it comes to
45+ changing state. The provider does what you would expect, automatically handling complex scenarios like deep merging.
4546
46- ``` javascript
47- // A component with a state provider
48- import Container from ' .. /container/Base.mjs' ;
49- import Label from ' .. /component/Label.mjs' ;
47+ ``` javascript live-preview
48+ import Button from ' neo.mjs/src/button/Base.mjs ' ;
49+ import Container from ' neo.mjs/src /container/Base.mjs' ;
50+ import Label from ' neo.mjs/src /component/Label.mjs' ;
5051
5152class MainView extends Container {
5253 static config = {
54+ className: ' My.StateProvider.Example1' ,
5355 stateProvider: {
5456 data: {
5557 user: {
56- firstname : ' Tobias' ,
57- lastname : ' Uhlig'
58+ firstName : ' Tobias' ,
59+ lastName : ' Uhlig'
5860 }
5961 }
6062 },
63+ layout: {ntype: ' vbox' , align: ' start' },
6164 items: [{
6265 module: Label,
6366 bind: {
64- // Bind the label's text to the user's full name
65- text : data => ` ${ data .user .firstname } ${ data .user .lastname } `
67+ text : data => ` User: ${ data .user .firstName } ${ data .user .lastName } `
68+ },
69+ style: {marginBottom: ' 10px' }
70+ }, {
71+ module: Button,
72+ text: ' Change First Name' ,
73+ handler () {
74+ // This performs a DEEP MERGE, not an overwrite.
75+ // The 'lastName' property will be preserved.
76+ this .setState ({
77+ user: { firstName: ' John' }
78+ });
79+ }
80+ }, {
81+ module: Button,
82+ text: ' Change Last Name (Path-based)' ,
83+ style: {marginTop: ' 10px' },
84+ handler () {
85+ // You can also set a value using its path.
86+ this .setState ({' user.lastName' : ' Doe' });
6687 }
6788 }]
6889 }
6990}
91+ MainView = Neo .setupClass (MainView);
7092```
93+ Notice the "Change First Name" button. It calls ` setState ` with an object that only contains ` firstName ` . The v10 provider
94+ is smart enough to perform a deep merge, updating ` firstName ` while leaving ` lastName ` untouched. This prevents accidental
95+ data loss and makes state updates safe and predictable by default.
7196
72- Where the magic truly begins is in how you * change* that data. Thanks to the new deep, proxy-based reactivity system,
73- you can modify state with plain JavaScript assignments. It's as simple as it gets:
97+ ### 3. The Power of Formulas: Derived State Made Easy
7498
75- ``` javascript
76- // Get the provider and change the data directly
77- const provider = myComponent .getStateProvider ();
99+ Because the provider is built on ` Neo.core.Effect ` , creating computed properties ("formulas") is a native, first-class
100+ feature. You define them in a separate ` formulas ` config, and the provider automatically keeps them updated.
78101
79- // This one line is all it takes to trigger a reactive update.
80- provider .data .user .firstname = ' Max' ;
102+ ``` javascript live-preview
103+ import Container from ' neo.mjs/src/container/Base.mjs' ;
104+ import Label from ' neo.mjs/src/component/Label.mjs' ;
105+ import TextField from ' neo.mjs/src/form/field/Text.mjs' ;
81106
82- // Does not overwrite the lastname
83- provider .setData ({user: {firstname: ' Robert' }})
107+ class MainView extends Container {
108+ static config = {
109+ className: ' My.StateProvider.Example2' ,
110+ layout: {ntype: ' vbox' , align: ' stretch' },
111+ stateProvider: {
112+ data: {
113+ user: {
114+ firstName: ' Tobias' ,
115+ lastName : ' Uhlig'
116+ }
117+ },
118+ formulas: {
119+ fullName : data => ` ${ data .user .firstName } ${ data .user .lastName } `
120+ }
121+ },
122+ items: [{
123+ module: Label,
124+ bind: { text : data => ` Welcome, ${ data .fullName } !` },
125+ style: {marginBottom: ' 10px' }
126+ }, {
127+ module: TextField,
128+ labelText: ' First Name' ,
129+ bind: { value : data => data .user .firstName },
130+ listeners: {
131+ change : function ({value}) { this .setState ({' user.firstName' : value}) }
132+ }
133+ }, {
134+ module: TextField,
135+ labelText: ' Last Name' ,
136+ bind: { value : data => data .user .lastName },
137+ listeners: {
138+ change : function ({value}) { this .setState ({' user.lastName' : value}) }
139+ }
140+ }]
141+ }
142+ }
143+ MainView = Neo .setupClass (MainView);
144+ ```
145+ When you edit the text fields, the ` setState ` call updates the base ` user ` data. The ` Effect ` system detects this,
146+ automatically re-runs the ` fullName ` formula, and updates the welcome label.
84147
85- // You can update multiple properties at once. Thanks to automatic batching,
86- // this results in only a single UI update cycle.
87- provider .setData ({user: {firstname: ' John' , lastname: ' Doe' }})
148+ ### 4. Hierarchical by Design: Nested Providers That Just Work
88149
89- // Alternative Syntax:
90- provider .setData ({
91- ' user.firstname' : ' John' ,
92- ' user.lastname' : ' Doe'
93- });
150+ The v10 provider was engineered to handle different scopes of state with an intelligent hierarchical model. A child
151+ component can seamlessly access data from its own provider as well as any parent provider.
152+
153+ ``` javascript live-preview
154+ import Container from ' neo.mjs/src/container/Base.mjs' ;
155+ import Label from ' neo.mjs/src/component/Label.mjs' ;
156+
157+ class MainView extends Container {
158+ static config = {
159+ className: ' My.StateProvider.Example3' ,
160+ stateProvider: {
161+ data: { theme: ' dark' }
162+ },
163+ layout: {ntype: ' vbox' , align: ' stretch' , padding: ' 10px' },
164+ items: [{
165+ module: Label,
166+ bind: { text : data => ` Global Theme: ${ data .theme } ` }
167+ }, {
168+ module: Container,
169+ stateProvider: {
170+ data: { user: ' Alice' }
171+ },
172+ border: true ,
173+ style: {padding: ' 10px' , marginTop: ' 10px' },
174+ items: [{
175+ module: Label,
176+ bind: {
177+ text : data => ` Local User: ${ data .user } (Theme: ${ data .theme } )`
178+ }
179+ }]
180+ }]
181+ }
182+ }
183+ MainView = Neo .setupClass (MainView);
94184```
185+ The nested component can access both ` user ` from its local provider and ` theme ` from the parent provider without any
186+ extra configuration.
95187
96- There are no special setter functions to call, no reducers to write. You just change the data, and the UI updates.
97- This clean, direct developer experience is the "what." Now, let's look at the "how."
188+ ### 5. The Final Piece: State Providers in Functional Components
189+
190+ Thanks to the v10 refactoring, state providers are now a first-class citizen in functional components. You can define a
191+ provider and bind to its data with the same power and simplicity as in class-based components.
192+
193+ ``` javascript live-preview
194+ import {defineComponent } from ' neo.mjs' ;
195+ import Label from ' neo.mjs/src/component/Label.mjs' ;
196+ import TextField from ' neo.mjs/src/form/field/Text.mjs' ;
197+
198+ export default defineComponent ({
199+ stateProvider: {
200+ data: {
201+ user: {
202+ firstName: ' Jane' ,
203+ lastName : ' Doe'
204+ }
205+ },
206+ formulas: {
207+ fullName : data => ` ${ data .user .firstName } ${ data .user .lastName } `
208+ }
209+ },
210+ createVdom (config ) {
211+ return {
212+ layout: {ntype: ' vbox' , align: ' stretch' },
213+ items: [{
214+ module: Label,
215+ bind: { text : data => ` Welcome, ${ config .data .fullName } !` },
216+ style: {marginBottom: ' 10px' }
217+ }, {
218+ module: TextField,
219+ labelText: ' First Name' ,
220+ bind: { value : data => config .data .user .firstName },
221+ listeners: {
222+ change : ({value}) => config .setState ({' user.firstName' : value})
223+ }
224+ }, {
225+ module: TextField,
226+ labelText: ' Last Name' ,
227+ bind: { value : data => config .data .user .lastName },
228+ listeners: {
229+ change : ({value}) => config .setState ({' user.lastName' : value})
230+ }
231+ }]
232+ }
233+ }
234+ });
235+ ```
236+ This example demonstrates the full power of the new architecture: a functional component with its own reactive data,
237+ computed properties, and two-way bindings, all with clean, declarative code.
98238
99- ### 3 . From Theory to Practice: The Comprehensive Guide
239+ ### 6 . From Theory to Practice: The Comprehensive Guide
100240
101241The examples above show the clean, intuitive API. For a complete, hands-on exploration with dozens of live-preview
102242examples covering everything from nested providers and formulas to advanced store management, we encourage you to
@@ -105,7 +245,7 @@ this system possible.
105245
106246** [ Read the Full State Providers Guide Here] ( ../guides/datahandling/StateProviders.md ) **
107247
108- ### 4 . Under the Hood Part 1: The Proxy's Magic
248+ ### 7 . Under the Hood Part 1: The Proxy's Magic
109249
110250The beautiful API above is powered by a sophisticated proxy created by ` Neo.state.createHierarchicalDataProxy ` .
111251When you interact with ` provider.data ` , you're not touching a plain object; you're interacting with an intelligent agent
@@ -125,7 +265,7 @@ Here’s how it works:
125265
126266This proxy is the bridge between a simple developer experience and a powerful, fine-grained reactive engine.
127267
128- ### 5 . Under the Hood Part 2: The "Reactivity Bubbling" Killer Feature
268+ ### 8 . Under the Hood Part 2: The "Reactivity Bubbling" Killer Feature
129269
130270This is where the Neo.mjs ` state.Provider ` truly shines and solves the "Context Tax." Consider this critical question:
131271
0 commit comments