Skip to content

Commit cb8f487

Browse files
committed
Update the State Providers guide to reflect effects-based reactivity and direct store binding #7008
1 parent 9ea7745 commit cb8f487

1 file changed

Lines changed: 62 additions & 14 deletions

File tree

learn/guides/datahandling/StateProviders.md

Lines changed: 62 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,61 @@ Rules of thumb:
88

99
Other libraries or frameworks often call state providers "Stores".
1010

11+
## How Reactivity Works: The Effects-Based System
12+
13+
Neo.mjs State Providers implement a powerful effects-based reactivity system,
14+
which automatically tracks dependencies and re-evaluates computations when
15+
their underlying data changes. This system is built around two core concepts:
16+
17+
### 1. The `Neo.core.Effect` Class
18+
19+
At the heart of the reactivity is the `Neo.core.Effect` class. An `Effect`
20+
is a mechanism that encapsulates a function (its "effect function") and
21+
automatically re-runs this function whenever any of the reactive data
22+
properties it accesses change.
23+
24+
* **Implicit Dependency Tracking:** When your effect function (e.g., a formula
25+
or a binding formatter) reads a reactive data property (e.g., `data.user.firstName`),
26+
the `Effect` automatically "subscribes" to changes in that property.
27+
* **Automatic Re-evaluation:** If `data.user.firstName` changes, the `Effect`
28+
detects this and automatically re-executes its effect function, ensuring
29+
that any dependent computations or UI updates are performed.
30+
31+
### 2. The Hierarchical Data Proxy
32+
33+
When you access data within a State Provider (e.g., in a `bind` configuration
34+
or a `formula`), you are interacting with a special `Proxy` object. This proxy
35+
is created by `Neo.state.createHierarchicalDataProxy` and provides a unified
36+
view of data across the entire State Provider hierarchy (current provider and
37+
all its parents).
38+
39+
* **Seamless Data Access:** You can access any data property, whether it lives
40+
in the current State Provider or a parent, using simple dot notation (e.g.,
41+
`data.myProperty` or `data.user.address.street`).
42+
* **Enabling Dependency Tracking:** This proxy works in conjunction with
43+
`Neo.core.Effect` to enable implicit dependency tracking. When an `Effect`
44+
reads a property through this proxy, the proxy notifies the `Effect` about
45+
the access, allowing the `Effect` to register that property as a dependency.
46+
47+
### How Bindings and Formulas Utilize Effects
48+
49+
* **Bindings (`bind` config):** When you define a `bind` configuration for a
50+
component (e.g., `bind: {text: data => data.hello}`), Neo.mjs creates an
51+
`Effect`. The effect function is your formatter (`data => data.hello`).
52+
Whenever `data.hello` changes, the `Effect` re-runs, re-evaluates the
53+
formatter, and updates the component's `text` config.
54+
55+
* **Formulas (`formulas` config):** Similarly, for `formulas` defined in a
56+
State Provider, each formula function is wrapped in an `Effect`. When the
57+
formula accesses data (e.g., `data.a + data.b`), the `Effect` tracks `data.a`
58+
and `data.b` as dependencies. If either changes, the `Effect` re-runs the
59+
formula, and its computed result is automatically updated in the State Provider's
60+
data.
61+
62+
This effects-based system significantly reduces boilerplate, making state
63+
management intuitive and efficient by handling dependency tracking and updates
64+
automatically.
65+
1166
## Inline State Providers
1267
### Direct Bindings
1368
```javascript live-preview
@@ -111,9 +166,9 @@ Inside the Container are 3 Labels which bind their `text` config to a combinatio
111166

112167
We are showcasing 3 different ways how you can define your binding (resulting in the same output).
113168

114-
In case any of the bound data props changes, all bound Configs will check for an update.
169+
In case any of the bound data props changes, the underlying `Effect` for that binding will re-evaluate, and all bound Configs will update.
115170

116-
Important: The Config setter will only trigger in case there is a real change for the bound output.
171+
Important: The Config setter will only trigger in case there is a real change for the bound output, ensuring efficient updates.
117172

118173
We also added 2 Buttons to change the value of each data prop, so that we can see that the bound Label texts
119174
update right away.
@@ -252,7 +307,7 @@ which contains the nested props `firstname` and `lastname`.
252307
We can bind to these nested props like before:</br>
253308
`bind: {text: data => data.user.firstname + ' ' + data.user.lastname}`
254309

255-
Any change of a nested data prop will directly get reflected into the bound components.
310+
Any change of a nested data prop will directly trigger the associated `Effect` and get reflected into the bound components.
256311

257312
We can update a nested data prop with passing its path:</br>
258313
`data => data.component.setState({'user.lastname': 'Rahder'})`
@@ -498,15 +553,10 @@ class MainViewStateProvider extends StateProvider {
498553
static config = {
499554
className: 'Guides.vm7.MainViewStateProvider',
500555

501-
data: {
502-
myStoreCount: 0
503-
},
504-
505556
stores: {
506557
// Define a store using a class reference
507558
mySharedStore: {
508-
module : MyDataStore,
509-
listeners: {countChange: 'onMyStoreCountChange'}
559+
module : MyDataStore
510560
},
511561
// Define another store using an inline configuration
512562
anotherStore: {
@@ -522,11 +572,9 @@ class MainViewStateProvider extends StateProvider {
522572
{value: 30}
523573
]
524574
}
525-
}
526-
}
575+
},
527576

528-
onMyStoreCountChange(data) {
529-
this.data.myStoreCount = data.value // Reactive
577+
530578
}
531579
}
532580
MainViewStateProvider = Neo.setupClass(MainViewStateProvider);
@@ -557,7 +605,7 @@ class MainView extends Container {
557605
module: Label,
558606
style : {margin: 'auto'},
559607
bind: {
560-
text: data => `Count: ${data.myStoreCount}`
608+
text: data => `Count: ${data.stores.mySharedStore.count}`
561609
}
562610
}, {
563611
module: Button,

0 commit comments

Comments
 (0)