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.
- package/package.json +1 -1
- package/readme.md +38 -16
package/package.json
CHANGED
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
|
|
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
|
[](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
|
[](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
|
-
|
|
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
|
-
|
|
83
|
-
|
|
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
|
-
###
|
|
99
|
-
|
|
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
|
|
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 <input/> 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
|
-
-
|
|
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
|
- <Input/> bind(...)ing
|
|
178
200
|
|