react-deepwatch 1.2.6 → 1.3.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 (2) hide show
  1. package/package.json +1 -1
  2. package/readme.md +38 -16
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-deepwatch",
3
- "version": "1.2.6",
3
+ "version": "1.3.0",
4
4
  "description": "",
5
5
  "keywords": [
6
6
  "react",
package/readme.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # React Deepwatch - no more setState and less
2
2
 
3
3
 
4
- **Deeply watches your state-object and props** for changes. **Re-renders** automatically😎 and makes you write less code 😊.
4
+ **Deeply watches your state-object and props** for changes. **Re-renders** automatically😎 and makes you write much less code😊.
5
5
  - **Performance friendly**
6
6
  React Deepwatch uses a [proxy-facade](https://github.com/bogeeee/proxy-facades) to **watch only for those properties that are actually used** in your component function. It doesn't matter how complex and deep the graph behind your state or props is.
7
7
  - **Can watch your -model- as well**
@@ -24,7 +24,7 @@ const MyComponent = watchedComponent(props => {
24
24
  <input type="button" value="Clear filter" onClick={() => state.filter = ""} />
25
25
 
26
26
  {/* you can fetch data from **inside** conditional render code or loops😎! No useEffect needed! Knows its dependencies automatically👍 */}
27
- <div>Here are the fruits, fetched from the Server:<br/><i>{ load(async ()=> await simulateFetchFruitsFromServer(state.filter), {fallback:"loading list 🌀"} )}</i></div><br/>
27
+ <div>Here are the fruits, fetched from the Server:<br/><i>{ load(async ()=> await fetchFruitsFromServer(state.filter), {fallback:"loading list 🌀"} )}</i></div><br/>
28
28
 
29
29
  {/* The above load(...) code is independent of state.showPrices, react-deepwatch knows that automatically, so clicking here will NOT exec a re- load(...)👍... */}
30
30
  Show prices <input type="checkbox" {...bind(state.showPrices)} />
@@ -61,10 +61,10 @@ const MyComponent = watchedComponent(props => {
61
61
  [![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/fork/github/bogeeee/react-deepwatch/tree/1.x/examples/no-more-setstate?title=react-deepwatch%20example&file=index.jsx)
62
62
 
63
63
  ## and less... loading code
64
- Now that we already have the ability to deeply record our reads, let's see if there's also a way to **cut away the boilerplate code for `useEffect`**.
64
+ Now that we already have the ability to deeply record our reads, let's see if there's also a way to **cut away the boilerplate code for `useEffect`**:
65
65
 
66
66
  ````jsx
67
- import {watchedComponent, load, poll, isLoading, loadFailed, preserve} from "react-deepwatch"
67
+ import {watchedComponent, load, poll, isLoading, loadFailed, preserve, READS_INSIDE_LOADER_FN} from "react-deepwatch"
68
68
 
69
69
  const MyComponent = watchedComponent(props => {
70
70
 
@@ -77,10 +77,35 @@ const MyComponent = watchedComponent(props => {
77
77
  ````
78
78
  [![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/fork/github/bogeeee/react-deepwatch/tree/1.x/examples/less-loading-code?title=react-deepwatch%20example&file=index.jsx)
79
79
 
80
- **`load(...)` re-executes `myFetchFromServer`, when a dependent value changes**. That means, it records all reads from previous code in your component function _(which can as well be the result of a previous `load(...)` call)_ plus the reads immediately inside the `load(...)` call. _Here: props.myProperty._
81
- The returned Promise will be await'ed and the component will be put into [suspense](https://react.dev/reference/react/Suspense) that long.
82
- 👍 load(...) can be inside a conditional block or a loop. Then it has already recorded the condition + everything else that leads to the computation of load(...)'s point in time and state 😎._
83
- For this mechanic to work, **make sure, all sources are watched**: `props` and `load(...)`'s result are already automatically watched; For state, use `useWatchedState(...)`; For context, use `watched(useContext(...))`.
80
+ Note: 👍 load(...) even can be inside a conditional block or a loop 👍.
81
+ The returned Promise will be await'ed and the component will be put into [suspense](https://react.dev/reference/react/Suspense) that long.
82
+ **`load(...)` re-executes `myFetchFromServer`, when a dependent value changes**. For this auto-dependency mechanic to work, **make sure, all sources to your component are watched**: `props` and other `load(...)`'s result are already automatically watched; For state, use `useWatchedState(...)`; For context, use `watched(useContext(...))`.
83
+
84
+ ### Dependencies
85
+ By default, everything prior to the load(...) statement in your code and immediately in your loaderFn is treated as a dependency.
86
+ This is the perfectly safe and care free option for most use cases. But it can sometimes be too broad and lead to more reloads than necessary. Therefore, when you do performance sensitive fetches, you can fine-tune the dependencies:
87
+
88
+ <details>
89
+ <summary>Fine-tuning your deps</summary>
90
+
91
+ Just like with React's `useEffect(..., [...yourDeps...])`, You can explicitly specify the deps in the LoadOptions#deps array.
92
+ Additional, there is one special symbol which you can insert there:` READS_INSIDE_LOADER_FN`. This will treat trackable reads on objects in your loaderFn as dependencies. Examples:
93
+ ````jsx
94
+ const someWatchedObj = ... //somehow derived (deeply) from props, watched(...) or useWatchedState(...) or load(...)
95
+ const showPrices = state.showPrices;
96
+
97
+ {load(async () => {return await fetchFruitsFromServer(showPrices)}) } // ✅ Auto dependencies. This will include the recorded "read" in the line 'const showPrices = state.showPrices;' and therefore reload when showPrices is different (=any determnistic value that's derived from the inputs till here, which covers everything you could need👍).
98
+ {load(async () => {return await fetchFruitsFromServer(state.showPrices )}, {deps: [READS_INSIDE_LOADER_FN ]}) } // ✅
99
+ {load(async () => {return await fetchFruitsFromServer(someWatchedObj.users.get("Axel").getPrefs().showPrices)}, {deps: [READS_INSIDE_LOADER_FN]}) } // ✅ Depending on properties, starting from **on** a watched object/Array/Set/Map or derived stuff is all tracked (Thanks to proxy-facades, it will follow and track this complete path of reads and watch for changes precisely there👍).
100
+ {load(async () => {return await fetchFruitsFromServer(showPrices )}, {deps: [READS_INSIDE_LOADER_FN ]}) } // ❌ Reading the closured variable showPrices is not trackable by react-deepwatch. It can only track reads **on** (proxied-) objects = You should see a `.` or a `[...]`
101
+ {load(async () => {return await fetchFruitsFromServer(showPrices )}, {deps: [READS_INSIDE_LOADER_FN, showPrices]}) } // ✅ List showPrices additionally as depExplicitly. Note: READS_INSIDE_LOADER_FN is not needed here, but it doesn't hurt.
102
+
103
+ {load(async () => {
104
+ const fruits = await fetchFruitsFromServer(); // Takes some time...
105
+ return fruits.filter(f => f.indexOf(state.myFilter) >=0); // ❌ READS_INSIDE_LOADER_FN cant't catch the state.myFilter read, cause it did not happen **immediately** but after an async fork/not in the same sync block. You have to keep this in mind. Also for the auto-dependencies.
106
+ }, {deps: [READS_INSIDE_LOADER_FN]}) }
107
+ ````
108
+ </details>
84
109
 
85
110
  ### Show a 🌀loading spinner
86
111
  To show a 🌀loading spinner / placeholder during load, either...
@@ -95,13 +120,10 @@ either...
95
120
  - **try/catch around the load(...)** statement. Caveat: You must check, if caught is `instanceof Promise` and re-throw it then. _Because this is the way for `load` to signal, that things are loading._ _Or..._
96
121
  - **call** the **loadFailed()** probing function. This looks more elegant than the above. _See jsDoc for usage example._
97
122
 
98
- ### Performance optimization for load(...)
99
- To reduce the number of expensive `myFetchFromServer` calls, try the following:
100
- - Move the load(...) call as upwards in the code as possible, so it depends on fewer props / state / watched objects.
101
- - See the `LoadOptions#fallback`, `LoadOptions#silent` and `LoadOptions#critical` settings.
102
- - Use the `preserve` function on all your fetched data, to smartly ensure non-changing object instances in your app (`newFetchResult` **===** `oldFetchResult`; Triple-equals. Also for the deep result_). Changed object instances can either cascade to a lot of re-loads or result in your component still watching the old instance.
123
+ ### Object instance preserving
124
+ Use the `preserve` function on all your fetched data, to smartly ensure non-changing object instances in your app (`newFetchResult` **===** `oldFetchResult`; Triple-equals. Also for the deep result_). Changed object instances can either cascade to a lot of re-loads or result in your component still watching the old instance.
103
125
  _Think of it like: The preserve function does for your data, what React does for your component tree: It smartly remembers the instances, if needed with the help of an id or key, and re-applies the re-fetched/re-rendered properties to them, so the object-identity/component-state stays the same._
104
- 👍 `load(...)` does call `preserve` by default to enforce this paradigm and give you the best, trouble free experience.
126
+ 👍 `load(...)` does `preserve` its result by default to enforce this paradigm and give you the best, trouble free experience.
105
127
 
106
128
  ### Caveats
107
129
  - The component function might return and empty `</>` on the first load and **produce a short screen flicker**. This is [because React's Suspense mechasim is not able to remeber state at that time](https://react.dev/reference/react/Suspense#caveats). To circumvent this, specify `WatchedComponentOptions#fallback`.
@@ -137,7 +159,7 @@ const MyParentComponent = watchedComponent(props => {
137
159
 
138
160
  _This example will trigger both onChange handlers._
139
161
 
140
- _Note, that `watched(myState.form) !== myState.form`. It created a new proxy object in a new proxy-facade layer here, just for the purpose of deep-watching everything under it. Sometimes you may want to take advantage of it, so that modifications in the originaly layer (in MyParentComponent) won't fire the onChange event / call the postFormToTheSerer function. I.e. for updates that came from the server_
162
+ _Note, that `watched(myState.form) !== myState.form`. It created a new proxy object in a new proxy-facade layer here, just for the purpose of deep-watching everything under it. Keep that in mind, when i.e. comparing objects by instance (I.e. row === selectedRow) if they arrived in different ways. Sometimes you may want to take advantage of it, so that modifications in the originaly layer (in MyParentComponent) won't fire the onChange event / call the postFormToTheSerer function. I.e. for updates that **came** from the server_
141
163
 
142
164
  # ...and less onChange code for &lt;input/&gt; elements
143
165
 
@@ -172,7 +194,7 @@ There are also other libraries that address proxying the state:
172
194
  while React-deepwatch set's its self apart in these areas:
173
195
  - Deep (not only shallow-) proxying
174
196
  - Tracking changes above **and** below the proxy = also on the unproxied object.
175
- - Fully transparent support for `this`, getters/setters (treated white box), user's methods, Sets, Maps, Arrays _(wana seems to support Sets,Maps, Arrays too)_
197
+ - Full transparent support for `this`, getters/setters (treated as white box), user's methods, Sets, Maps, Arrays _(wana seems to support Sets,Maps, Arrays too)_
176
198
  - Very comprehensive `load(...)` concept with auto dependencies, fallbacks, probing functions, instance preserving mechanism, possible in conditionals/loops, supports polling, error boundaries.
177
199
  - &lt;Input/&gt; bind(...)ing
178
200