@webkrafters/react-observable-context 4.0.0 → 4.1.0-alpha.1
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/README.md +286 -87
- package/dist/constants.d.ts +8 -1
- package/dist/constants.js +16 -2
- package/dist/main/hooks/use-state-manager/index.js +2 -2
- package/dist/main/hooks/use-store/index.d.ts +2 -1
- package/dist/main/hooks/use-store/index.js +5 -6
- package/dist/main/index.d.ts +10 -9
- package/dist/main/set-state/index.d.ts +4 -4
- package/dist/main/set-state/index.js +94 -67
- package/dist/main/set-state/tag-functions/index.d.ts +37 -0
- package/dist/main/set-state/tag-functions/index.js +348 -0
- package/dist/model/accessor-cache/index.d.ts +1 -1
- package/dist/model/accessor-cache/index.js +7 -8
- package/dist/model/atom/index.js +1 -3
- package/dist/model/storage/index.js +2 -3
- package/dist/types.d.ts +48 -4
- package/dist/utils/clonedeep-eligibility-check.d.ts +7 -0
- package/dist/utils/clonedeep-eligibility-check.js +52 -0
- package/dist/utils/index.d.ts +7 -1
- package/dist/utils/index.js +126 -4
- package/package.json +7 -3
package/README.md
CHANGED
|
@@ -80,14 +80,7 @@ A property path is a dot-notation string leading to a specific property within a
|
|
|
80
80
|
<strong id="property-path-example">Ex. Given the following object:</strong>
|
|
81
81
|
|
|
82
82
|
```jsx
|
|
83
|
-
{
|
|
84
|
-
a: {
|
|
85
|
-
c: {
|
|
86
|
-
e: 5,
|
|
87
|
-
f: [ 0, 2, 4 ]
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
}
|
|
83
|
+
{ a: { c: { e: 5, f: [ 0, 2, 4 ] } } }
|
|
91
84
|
```
|
|
92
85
|
The property path `a.c.e` accesses the `e=5` property.<br />
|
|
93
86
|
Either of the property paths `a.c.f.1` and `a.c.f[1]` accesses the `[1]=2` property.<br />
|
|
@@ -112,29 +105,28 @@ A selector map is an object holding key:value pairs.<br />
|
|
|
112
105
|
```jsx
|
|
113
106
|
// Given the following state object:
|
|
114
107
|
const state = {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
z: 9
|
|
124
|
-
} ]
|
|
125
|
-
}
|
|
108
|
+
a: 1, b: 2, c: 3, d: {
|
|
109
|
+
e: 5,
|
|
110
|
+
f: [ 6, {
|
|
111
|
+
x: 7,
|
|
112
|
+
y: 8,
|
|
113
|
+
z: 9
|
|
114
|
+
} ]
|
|
115
|
+
}
|
|
126
116
|
};
|
|
117
|
+
|
|
127
118
|
// a client observing the following selector map
|
|
128
119
|
const selectorMap = {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
120
|
+
all: '@@STATE',
|
|
121
|
+
myData: 'd',
|
|
122
|
+
secondFElement: 'd.f[1]'
|
|
132
123
|
};
|
|
124
|
+
|
|
133
125
|
// will receive the following store data
|
|
134
126
|
store.data = {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
127
|
+
all: state,
|
|
128
|
+
myData: state.d,
|
|
129
|
+
secondFElement: state.d.f[1]
|
|
138
130
|
}
|
|
139
131
|
```
|
|
140
132
|
|
|
@@ -207,6 +199,199 @@ store.setState({ ...state, a: { ...state.a, b: [ { ...first, y: 30 }, 22, ...res
|
|
|
207
199
|
// Refrain from doing this, please!
|
|
208
200
|
```
|
|
209
201
|
|
|
202
|
+
<h3 id="setstate-tags"><b><i><u>Rewriting state using tag commands</u></i></b></h3>
|
|
203
|
+
By default <code>store.setState</code> merges new changes into current state. To overwrite current state slices with new state values, <b>7</b> tag commands have been provided for:
|
|
204
|
+
<ol>
|
|
205
|
+
<li><span style="margin-left: 10px"><code>@@CLEAR:</code> setting state slice to its corresponding empty value</span></li>
|
|
206
|
+
<li><span style="margin-left: 10px"><code>@@DELETE:</code> deleting properties</span></li>
|
|
207
|
+
<li><span style="margin-left: 10px"><code>@@MOVE:</code> moving array elements</span></li>
|
|
208
|
+
<li><span style="margin-left: 10px"><code>@@PUSH:</code> pushing new items into an array</span></li>
|
|
209
|
+
<li><span style="margin-left: 10px"><code>@@REPLACE:</code> replacing property values</span></li>
|
|
210
|
+
<li><span style="margin-left: 10px"><code>@@SET:</code> setting property values</span></li>
|
|
211
|
+
<li><span style="margin-left: 10px"><code>@@SPLICE:</code> splicing array items</span></li>
|
|
212
|
+
</ol>
|
|
213
|
+
<b>Examples:</b><br /><br />
|
|
214
|
+
|
|
215
|
+
<i><b>@@CLEAR:</b> (takes no arguments)</i>
|
|
216
|
+
|
|
217
|
+
```jsx
|
|
218
|
+
const state = {
|
|
219
|
+
a: { b: [{ x: 7, y: 8, z: 9 }, { x: 17, y: 18, z: 19 }] },
|
|
220
|
+
j: 10
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
/* empties the state; sets state = {} */
|
|
224
|
+
store.setState( '@@CLEAR' ) // or store.setState({ '@@CLEAR': <anything> })
|
|
225
|
+
|
|
226
|
+
/* empties the value at state.a.b; sets state.a.b = [] */
|
|
227
|
+
store.setState({ a: { b: '@@CLEAR' } }) // or store.setState({ a: { b: { '@@CLEAR': <anything> } } })
|
|
228
|
+
|
|
229
|
+
/* empties the value at state.a.j; sets state.a.j = null */
|
|
230
|
+
store.setState({ a: { j: '@@CLEAR' } }) // or store.setState({ a: { j: { '@@CLEAR': <anything> } } })
|
|
231
|
+
|
|
232
|
+
/* empties the value at state.a.b[ 0 ]; sets state.a.b = [{}] */
|
|
233
|
+
store.setState({ a: { b: [ '@@CLEAR' ] } }) // or store.setState({ a: { b: [ { '@@CLEAR': <anything> } ] } })
|
|
234
|
+
|
|
235
|
+
/* empties the value at state.a.b[0]; sets state.a.b = [{}, state.a.b[1]] */
|
|
236
|
+
store.setState({ a: { b: [ '@@CLEAR', state.a.b[1] ] } }) // or store.setState({ a: { b: [ { '@@CLEAR': <anything> }, state.a.b[1] ] } })
|
|
237
|
+
|
|
238
|
+
/* empties the value at state.a.b[0]; sets state.a.b = [{}, a.b[1]] using indexing (RECOMMENDED) */
|
|
239
|
+
store.setState({ a: { b: { 0: '@@CLEAR' } } }) // or store.setState({ a: { b: { 0: { '@@CLEAR': <anything> } } } })
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
<i><b>@@DELETE:</b> (takes an array argument listing property keys to delete)</i>
|
|
243
|
+
|
|
244
|
+
```jsx
|
|
245
|
+
const state = {
|
|
246
|
+
a: { b: [{ x: 7, y: 8, z: 9 }, { x: 17, y: 18, z: 19 }] },
|
|
247
|
+
j: 10
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
store.setState({ '@@DELETE': [ 'a' ] }) // removes state.a; sets state = {j: 10}
|
|
251
|
+
|
|
252
|
+
store.setState({ a: { '@@DELETE': [ 'b' ] } }) // removes state.a.b; sets state.a = {}
|
|
253
|
+
|
|
254
|
+
/* removes state.a.b[0]; leaving state.a.b = [{ x: 17, y: 18, z: 19 }] */
|
|
255
|
+
store.setState({ a: { b: { '@@DELETE': [ 0 ] } } }) // or store.setState({ a: { b: { '@@DELETE': [ -2 ] } } })
|
|
256
|
+
|
|
257
|
+
/* removes `x` and `z` properties from state.a.b[1]; sets state.a.b = [{ x: 7, y: 8, z: 9 }, {y: 18}] */
|
|
258
|
+
store.setState({ a: { b: [ state.a.b[ 0 ], { '@@DELETE': [ 'x', 'z' ] } ] } })
|
|
259
|
+
|
|
260
|
+
/* removes `x` and `z` properties from state.a.b[1]; sets state.a.b = [{ x: 7, y: 8, z: 9 }, {y: 18}] using indexing (RECOMMENDED) */
|
|
261
|
+
store.setState({ a: { b: { 1: { '@@DELETE': [ 'x', 'z' ] } } } })
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
<i><b>@@MOVE:</b> (takes an array argument listing: -/+fromIndex, -/+toIndex and optional +numItems?. numItems = 1 by default)</i>
|
|
265
|
+
|
|
266
|
+
```jsx
|
|
267
|
+
const state = {
|
|
268
|
+
a: { b: [{ x: 7, y: 8, z: 9 }, { x: 17, y: 18, z: 19 }] },
|
|
269
|
+
j: 10,
|
|
270
|
+
q: [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
store.setState({ a: { '@@MOVE': [ 0, 1 ] } }) // assigning a '@@MOVE' command to a non-array property has no effect.
|
|
274
|
+
|
|
275
|
+
/* moves state.a.b[0] into index 1; leaving state.a.b = [{ x: 17, y: 18, z: 19 }, { x: 7, y: 8, z: 9 }] */
|
|
276
|
+
store.setState({ a: { b: { '@@MOVE': [ 0, 1 ] } } }) // or store.setState({ a: { b: { '@@MOVE': [ -2, -1 ] } } })
|
|
277
|
+
|
|
278
|
+
/* moves state.q[4] - [7] into indexes 1 - 4; leaving state.q = [ 1, 5, 6, 7, 8, 2, 3, 4, 9 ] */
|
|
279
|
+
store.setState({ a: { q: { '@@MOVE': [ 4, 1, 4 ] } } }) // or store.setState({ a: { q: { '@@MOVE': [ -5, -8, 4 ] } } })
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
<i><b>@@PUSH:</b> (takes an array argument listing new values to append)</i>
|
|
283
|
+
|
|
284
|
+
```jsx
|
|
285
|
+
const state = {
|
|
286
|
+
a: { b: [{ x: 7, y: 8, z: 9 }, { x: 17, y: 18, z: 19 }] },
|
|
287
|
+
j: 10
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
store.setState({ a: { '@@PUSH': [{ n: 5 }] } }) // assigning a '@@PUSH' command to a non-array property has no effect.
|
|
291
|
+
|
|
292
|
+
/* appends 2 new items into state.a.b; leaving state.a.b = [...state.a.b, { x: 27, y: 28, z: 29 }, { x: 37, y: 38, z: 39 }] */
|
|
293
|
+
store.setState({ a: { b: { '@@PUSH': [{ x: 27, y: 28, z: 29 }, { x: 37, y: 38, z: 39 }] } } })
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
<i><b>@@REPLACE:</b> (takes an argument holding the replacment value)</i>
|
|
297
|
+
|
|
298
|
+
```jsx
|
|
299
|
+
const state = {
|
|
300
|
+
a: { b: [{ x: 7, y: 8, z: 9 }, { x: 17, y: 18, z: 19 }] },
|
|
301
|
+
j: 10
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
store.setState({ '@@REPLACE': { a: 'Demo', j: 17 } }) // rewrites state to { a: 'Demo', j: 17 };
|
|
305
|
+
|
|
306
|
+
store.setState({ a: { '@@REPLACE': { message: 'Testing...' } } }) // rewrites state.a.b to { message: 'Testing...' }
|
|
307
|
+
|
|
308
|
+
/* rewrites state.a.b[1] to { x: 97, y: 98, z: 99 }; leaving state.a.b = [{ x: 7, y: 8, z: 9 }, { x: 97, y: 98, z: 99 }] */
|
|
309
|
+
store.setState({ a: { b: [ state.a.b[ 0 ], { '@@REPLACE': { x: 97, y: 98, z: 99 } } ] } })
|
|
310
|
+
|
|
311
|
+
/* rewrites state.a.b[1] to { x: 97, y: 98, z: 99 }; leaving state.a.b = [{ x: 7, y: 8, z: 9 }, { x: 97, y: 98, z: 99 }] using indexing (RECOMMENDED) */
|
|
312
|
+
store.setState({ a: { b: { 1: { '@@REPLACE': { x: 97, y: 98, z: 99 } } } } })
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
<i><b>@@SET:</b> (takes an argument holding either the replacment value or a compute function returning the replacement value)</i>
|
|
316
|
+
|
|
317
|
+
```jsx
|
|
318
|
+
/*
|
|
319
|
+
This tag is for handling edge cases only. Please use sparingly. In most cases, store.setState with or without any of the other tags is sufficient and most efficient.
|
|
320
|
+
|
|
321
|
+
This and the '@@REPLACE' tags are functionally equivalent when used with a replacement value argument.
|
|
322
|
+
|
|
323
|
+
Be aware that the compute function argument may be `undefined` for properties which do not yet exist in the state.
|
|
324
|
+
*/
|
|
325
|
+
|
|
326
|
+
const state = {
|
|
327
|
+
a: { b: [{ x: 7, y: 8, z: 9 }, { x: 17, y: 18, z: 19 }] },
|
|
328
|
+
j: 10
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
store.setState({ '@@SET': currentValue => ({ ...currentValue, a: 'Demo', j: 17 }) }) // rewrites state to { ...state, a: 'Demo', j: 17 };
|
|
332
|
+
|
|
333
|
+
store.setState({ a: { '@@SET': currentValue => ({ ...currentValue, message: 'Testing...' }) } }) // rewrites state.a.b to { ...state, message: 'Testing...' }
|
|
334
|
+
|
|
335
|
+
/* rewrites state.a.b[1] to { x: 97, y: 98, z: 99 }; leaving state.a.b = [{ x: 7, y: 8, z: 9 }, { x: 97, y: 98, z: 99 }] */
|
|
336
|
+
store.setState({ a: { b: [ state.a.b[ 0 ], { '@@SET': currentValue => ({ ...currentValue, x: 97, y: 98, z: 99 }) } ] } })
|
|
337
|
+
|
|
338
|
+
/* rewrites state.a.b[1] to { x: 97, y: 98, z: 99 }; leaving state.a.b = [{ x: 7, y: 8, z: 9 }, { x: 97, y: 98, z: 99 }] using indexing (RECOMMENDED) */
|
|
339
|
+
store.setState({ a: { b: { 1: { '@@SET': currentValue => ({ ...currentValue, x: 97, y: 98, z: 99 }) } } } })
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
<i><b>@@SPLICE:</b> (takes an array argument listing: -/+fromIndex, +deleteCount and optional ...newItems? newItems = ...[] by default)</i>
|
|
343
|
+
|
|
344
|
+
```jsx
|
|
345
|
+
const state = {
|
|
346
|
+
a: { b: [{ x: 7, y: 8, z: 9 }, { x: 17, y: 18, z: 19 }] },
|
|
347
|
+
j: 10,
|
|
348
|
+
q: [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
store.setState({ a: { '@@SPLICE': [ 0, 1 ] } }) // assigning a '@@SPLICE' command to a non-array property has no effect.
|
|
352
|
+
|
|
353
|
+
/* removes state.a.b[0]; leaving state.a.b = [{ x: 17, y: 18, z: 19 }] */
|
|
354
|
+
store.setState({ a: { b: { '@@SPLICE': [ 0, 1 ] } } }) // or store.setState({ a: { b: { '@@SPLICE': [ -2, -1 ] } } })
|
|
355
|
+
|
|
356
|
+
/* replaces state.q[4] - [7] with 2 items; leaving state.q = [ 1, 2, 3, 4, 33, 88, 9 ] */
|
|
357
|
+
store.setState({ a: { q: { '@@SPLICE': [ 4, 4, 33, 88 ] } } }) // or store.setState({ a: { q: { '@@SPLICE': [ -5, 4, 33, 88 ] } } })
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
<h3><b><i>Combination Usage:</i></b></h3>
|
|
361
|
+
|
|
362
|
+
These tags may be used in combination with the default usage where all top-level tag command results in property are sequentially merged into state followed by the merging of the rest of the property changes.
|
|
363
|
+
|
|
364
|
+
<strong>Example:</strong>
|
|
365
|
+
|
|
366
|
+
```jsx
|
|
367
|
+
const state = {
|
|
368
|
+
a: { b: [{ x: 7, y: 8, z: 9 }, { x: 17, y: 18, z: 19 }] },
|
|
369
|
+
j: 10,
|
|
370
|
+
q: [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
store.setState({
|
|
374
|
+
a: {
|
|
375
|
+
b: {
|
|
376
|
+
/* evaluated 1st */ '@@DELETE': [ 0 ], // upon deleting state.a.b[0] -> state.a.b[1] becomes the new state.a.b[0]
|
|
377
|
+
/* evaluated 3rd */ 0: '@@CLEAR', // clear the new state.a.b[0]
|
|
378
|
+
/* evaluated 4th */ 2: { x: 47, y: 48, z: 49 }, // add new item at state.a.b[2],
|
|
379
|
+
/* evaluated 2md */ '@@PUSH': [{ x: 107, y: 108, z: 109 }] // appends state.a.b[1]
|
|
380
|
+
}
|
|
381
|
+
},
|
|
382
|
+
j: { '@@SET': currentValue => currentValue < 10 ? currentValue : 0 },
|
|
383
|
+
q: {
|
|
384
|
+
/* evaluated 1st */ '@@MOVE': [ 5, 3, 2 ],
|
|
385
|
+
/* evaluated 2md */ 12: 11
|
|
386
|
+
}
|
|
387
|
+
})
|
|
388
|
+
// => {
|
|
389
|
+
// a: { b: [{}, { x: 107, y: 108, z: 109 }, { x: 47, y: 48, z: 49 }] },
|
|
390
|
+
// j: 0,
|
|
391
|
+
// q: [ 1, 2, 3, 6, 7, 4, 5, 8, 9, <empty>, <empty>, <empty>, 11 ]
|
|
392
|
+
// }
|
|
393
|
+
```
|
|
394
|
+
|
|
210
395
|
# API
|
|
211
396
|
|
|
212
397
|
The React-Observable-Context module contains **4** exports namely:
|
|
@@ -252,13 +437,15 @@ The React-Observable-Context module contains **4** exports namely:
|
|
|
252
437
|
|
|
253
438
|
# Usage
|
|
254
439
|
|
|
255
|
-
<i><b
|
|
440
|
+
<i><u><b>context.js</b></u></i>
|
|
441
|
+
|
|
256
442
|
```jsx
|
|
257
443
|
import { createContext } from '@webkrafters/react-observable-context';
|
|
258
444
|
export default createContext();
|
|
259
445
|
```
|
|
260
446
|
|
|
261
|
-
<i><b
|
|
447
|
+
<i><u><b>ui.js</b></u> (connect method)</i>
|
|
448
|
+
|
|
262
449
|
```jsx
|
|
263
450
|
import React, { useCallback, useEffect } from 'react';
|
|
264
451
|
import { connect } from '@webkrafters/react-observable-context';
|
|
@@ -266,13 +453,13 @@ import ObservableContext from './context';
|
|
|
266
453
|
|
|
267
454
|
export const YearText = ({ data }) => ( <div>Year: { data.year }</div> );
|
|
268
455
|
export const YearInput = ({ data, setState, resetState }) => {
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
456
|
+
const onChange = useCallback( e => setState({
|
|
457
|
+
a: { b: { x: { y: { z: { 0: e.target.value } } } } }
|
|
458
|
+
}), [ setState ]);
|
|
459
|
+
useEffect(() => {
|
|
460
|
+
data.year > 2049 && resetState([ 'a.b.c' ]);
|
|
461
|
+
}, [ data.year ]);
|
|
462
|
+
return ( <div>Year: <input type="number" onChange={ onChange } /> );
|
|
276
463
|
};
|
|
277
464
|
|
|
278
465
|
const withConnector = connect( ObservablContext, { year: 'a.b.x.y.z[0]' } );
|
|
@@ -280,16 +467,17 @@ const Client1 = withConnector( YearText );
|
|
|
280
467
|
const Client2 = withConnector( YearInput );
|
|
281
468
|
|
|
282
469
|
const Ui = () => (
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
470
|
+
<div>
|
|
471
|
+
<Client1 />
|
|
472
|
+
<Client2 />
|
|
473
|
+
</div>
|
|
287
474
|
);
|
|
288
475
|
|
|
289
476
|
export default Ui;
|
|
290
477
|
```
|
|
291
478
|
|
|
292
|
-
<i><b
|
|
479
|
+
<i><u><b>ui.js</b></u> (useContext with memo method)</i>
|
|
480
|
+
|
|
293
481
|
```jsx
|
|
294
482
|
import React, { memo, useCallback, useEffect } from 'react';
|
|
295
483
|
import { useContext } from '@webkrafters/react-observable-context';
|
|
@@ -298,32 +486,33 @@ import ObservableContext from './context';
|
|
|
298
486
|
const selectorMap = { year: 'a.b.x.y.z[0]' };
|
|
299
487
|
|
|
300
488
|
const Client1 = memo(() => { // memoize to prevent 'no-change' renders from the parent.
|
|
301
|
-
|
|
302
|
-
|
|
489
|
+
const { data } = useContext( ObservableContext, selectorMap );
|
|
490
|
+
return ( <div>Year: { data.year }</div> );
|
|
303
491
|
});
|
|
304
492
|
|
|
305
493
|
const Client2 = memo(() => { // memoize to prevent 'no-change' renders from the parent.
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
494
|
+
const { data, setState, resetState } = useContext( ObservableContext, selectorMap );
|
|
495
|
+
const onChange = useCallback( e => setState({
|
|
496
|
+
a: { b: { x: { y: { z: { 0: e.target.value } } } } }
|
|
497
|
+
}), [ setState ]);
|
|
498
|
+
useEffect(() => {
|
|
499
|
+
data.year > 2049 && resetState([ 'a.b.c' ]);
|
|
500
|
+
}, [ data.year ]);
|
|
501
|
+
return ( <div>Year: <input type="number" onChange={ onChange } /> );
|
|
314
502
|
});
|
|
315
503
|
|
|
316
504
|
const Ui = () => (
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
505
|
+
<div>
|
|
506
|
+
<Client1 />
|
|
507
|
+
<Client2 />
|
|
508
|
+
</div>
|
|
321
509
|
);
|
|
322
510
|
|
|
323
511
|
export default Ui;
|
|
324
512
|
```
|
|
325
513
|
|
|
326
514
|
<i id="provider-usage"><b><u>provider.js</u></b></i>
|
|
515
|
+
|
|
327
516
|
```jsx
|
|
328
517
|
import React, { useEffect, useState } from 'react';
|
|
329
518
|
import ObservableContext from './context';
|
|
@@ -332,47 +521,47 @@ import Ui from './ui';
|
|
|
332
521
|
const DEFAULT_C = 36;
|
|
333
522
|
|
|
334
523
|
const updateHooks = {
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
524
|
+
resetState: ( ...args ) => {
|
|
525
|
+
console.log( 'resetting state with >>>> ', JSON.stringify( args ) );
|
|
526
|
+
return true;
|
|
527
|
+
},
|
|
528
|
+
setState: ( ...args ) => {
|
|
529
|
+
console.log( 'merging following into state >>>> ', JSON.stringify( args ) );
|
|
530
|
+
return true;
|
|
531
|
+
}
|
|
343
532
|
};
|
|
344
533
|
|
|
345
534
|
const storageStub = {
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
535
|
+
clone( data ) { return your_clone_function( data ) },
|
|
536
|
+
data: null,
|
|
537
|
+
getItem( key ) { return this.data },
|
|
538
|
+
removeItem( key ) { this.data = null },
|
|
539
|
+
setItem( key, data ) { this.data = data }
|
|
351
540
|
};
|
|
352
541
|
|
|
353
542
|
const Provider = ({ c = DEFAULT_C }) => {
|
|
354
543
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
544
|
+
const [ state, setState ] = useState(() => ({
|
|
545
|
+
a: { b: { c, x: { y: { z: [ 2022 ] } } } }
|
|
546
|
+
}));
|
|
547
|
+
|
|
548
|
+
useEffect(() => {
|
|
549
|
+
// similar to `store.setState`, use the following to update
|
|
550
|
+
// only the changed slice of the context internal state.
|
|
551
|
+
setState({ a: { b: { c } } });
|
|
552
|
+
// Do not do the following: it will override the context internal state.
|
|
553
|
+
// setState({ ...state, a: { ...state.a, b: { ...state.a.b, c } } });
|
|
554
|
+
}, [ c ]);
|
|
555
|
+
|
|
556
|
+
return (
|
|
557
|
+
<ObservableContext.Provider
|
|
558
|
+
prehooks={ updateHooks }
|
|
559
|
+
storage={ storageStub }
|
|
560
|
+
value={ state }
|
|
561
|
+
>
|
|
562
|
+
<Ui />
|
|
563
|
+
</ObservableContext.Provider>
|
|
564
|
+
);
|
|
376
565
|
};
|
|
377
566
|
Provider.displayName = 'Provider';
|
|
378
567
|
|
|
@@ -380,6 +569,7 @@ export default Provider;
|
|
|
380
569
|
```
|
|
381
570
|
|
|
382
571
|
<i><b><u>index.js</u></b></i>
|
|
572
|
+
|
|
383
573
|
```jsx
|
|
384
574
|
import React from 'react';
|
|
385
575
|
import ReactDOM from 'react-dom';
|
|
@@ -389,6 +579,15 @@ ReactDOM.render( <Provider />, document.getElementById( 'root' ) );
|
|
|
389
579
|
```
|
|
390
580
|
|
|
391
581
|
<h1 id="changes">What's Changed?</h1>
|
|
582
|
+
<b>v4.1.0</b>
|
|
583
|
+
<table>
|
|
584
|
+
<tbody>
|
|
585
|
+
<tr><td><b>1.</b></td><td>Added new setState <a href="#setstate-tags">tags</a> to facilitate state update operations.</td></tr>
|
|
586
|
+
</tbody>
|
|
587
|
+
</table>
|
|
588
|
+
<hr />
|
|
589
|
+
|
|
590
|
+
<b>v4.0.0</b>
|
|
392
591
|
<table>
|
|
393
592
|
<tbody>
|
|
394
593
|
<tr><td><b>1.</b></td><td>Added the <a href="#connect"><code>connect</code></a> function to facilitate the encapsulated context-usage method.</td></tr>
|
package/dist/constants.d.ts
CHANGED
|
@@ -1,2 +1,9 @@
|
|
|
1
|
+
export const CLEAR_TAG: "@@CLEAR";
|
|
2
|
+
export const DELETE_TAG: "@@DELETE";
|
|
1
3
|
export const FULL_STATE_SELECTOR: "@@STATE";
|
|
2
|
-
export const
|
|
4
|
+
export const MOVE_TAG: "@@MOVE";
|
|
5
|
+
export const NULL_STATE_SELECTOR: "";
|
|
6
|
+
export const PUSH_TAG: "@@PUSH";
|
|
7
|
+
export const REPLACE_TAG: "@@REPLACE";
|
|
8
|
+
export const SET_TAG: "@@SET";
|
|
9
|
+
export const SPLICE_TAG: "@@SPLICE";
|
package/dist/constants.js
CHANGED
|
@@ -2,8 +2,22 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", {
|
|
3
3
|
value: true
|
|
4
4
|
});
|
|
5
|
-
exports.NULL_STATE_SELECTOR = exports.FULL_STATE_SELECTOR = void 0;
|
|
5
|
+
exports.SPLICE_TAG = exports.SET_TAG = exports.REPLACE_TAG = exports.PUSH_TAG = exports.NULL_STATE_SELECTOR = exports.MOVE_TAG = exports.FULL_STATE_SELECTOR = exports.DELETE_TAG = exports.CLEAR_TAG = void 0;
|
|
6
|
+
var CLEAR_TAG = '@@CLEAR';
|
|
7
|
+
exports.CLEAR_TAG = CLEAR_TAG;
|
|
8
|
+
var DELETE_TAG = '@@DELETE';
|
|
9
|
+
exports.DELETE_TAG = DELETE_TAG;
|
|
6
10
|
var FULL_STATE_SELECTOR = '@@STATE';
|
|
7
11
|
exports.FULL_STATE_SELECTOR = FULL_STATE_SELECTOR;
|
|
12
|
+
var MOVE_TAG = '@@MOVE';
|
|
13
|
+
exports.MOVE_TAG = MOVE_TAG;
|
|
8
14
|
var NULL_STATE_SELECTOR = '';
|
|
9
|
-
exports.NULL_STATE_SELECTOR = NULL_STATE_SELECTOR;
|
|
15
|
+
exports.NULL_STATE_SELECTOR = NULL_STATE_SELECTOR;
|
|
16
|
+
var PUSH_TAG = '@@PUSH';
|
|
17
|
+
exports.PUSH_TAG = PUSH_TAG;
|
|
18
|
+
var REPLACE_TAG = '@@REPLACE';
|
|
19
|
+
exports.REPLACE_TAG = REPLACE_TAG;
|
|
20
|
+
var SET_TAG = '@@SET';
|
|
21
|
+
exports.SET_TAG = SET_TAG;
|
|
22
|
+
var SPLICE_TAG = '@@SPLICE';
|
|
23
|
+
exports.SPLICE_TAG = SPLICE_TAG;
|
|
@@ -4,7 +4,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
});
|
|
5
5
|
exports["default"] = void 0;
|
|
6
6
|
var _react = require("react");
|
|
7
|
-
var
|
|
7
|
+
var _utils = require("../../../utils");
|
|
8
8
|
var _accessorCache = _interopRequireDefault(require("../../../model/accessor-cache"));
|
|
9
9
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
|
|
10
10
|
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
|
|
@@ -15,7 +15,7 @@ function _iterableToArrayLimit(arr, i) { var _i = null == arr ? null : "undefine
|
|
|
15
15
|
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
|
|
16
16
|
var useStateManager = function useStateManager(initStateValue) {
|
|
17
17
|
var _useState = (0, _react.useState)(function () {
|
|
18
|
-
return (0,
|
|
18
|
+
return (0, _utils.clonedeep)(initStateValue);
|
|
19
19
|
}),
|
|
20
20
|
_useState2 = _slicedToArray(_useState, 1),
|
|
21
21
|
state = _useState2[0];
|
|
@@ -4,6 +4,7 @@ export namespace deps {
|
|
|
4
4
|
}
|
|
5
5
|
export default useStore;
|
|
6
6
|
export type IStorage<T extends import("../../../types").State> = import("../../../types").IStorage<T>;
|
|
7
|
+
export type UpdatePayload<T extends import("../../../types").State> = import("../../../types").UpdatePayload<PartialState<T>>;
|
|
7
8
|
export type PartialState<T extends import("../../../types").State> = import('../../../types').PartialState<T>;
|
|
8
9
|
export type Prehooks<T extends import("../../../types").State> = import("../../../types").Prehooks<T>;
|
|
9
10
|
export type StoreInternal<T extends import("../../../types").State> = import("../../../types").StoreInternal<T>;
|
|
@@ -15,7 +16,7 @@ declare function useStore<T extends import("../../../types").State>(prehooks: Pr
|
|
|
15
16
|
[propertyPaths: string]: Readonly<any>;
|
|
16
17
|
};
|
|
17
18
|
resetState: (propertyPaths?: string[]) => void;
|
|
18
|
-
setState: (changes: import("../../../types").PartialState<T
|
|
19
|
+
setState: (changes: import("../../../types").UpdatePayload<import("../../../types").PartialState<T>>) => void;
|
|
19
20
|
subscribe: (listener: import("../../../types").Listener<T>) => VoidFunction;
|
|
20
21
|
unlinkCache: (clientId: string) => void;
|
|
21
22
|
};
|
|
@@ -4,8 +4,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
});
|
|
5
5
|
exports.deps = exports["default"] = void 0;
|
|
6
6
|
var _react = require("react");
|
|
7
|
-
var _lodash = _interopRequireDefault(require("lodash.
|
|
8
|
-
var _lodash2 = _interopRequireDefault(require("lodash.isboolean"));
|
|
7
|
+
var _lodash = _interopRequireDefault(require("lodash.isboolean"));
|
|
9
8
|
var _uuid = require("uuid");
|
|
10
9
|
var _constants = require("../../../constants");
|
|
11
10
|
var _utils = require("../../../utils");
|
|
@@ -34,7 +33,7 @@ function runPrehook(prehooks, name, args) {
|
|
|
34
33
|
return true;
|
|
35
34
|
}
|
|
36
35
|
var res = prehooks[name].apply(prehooks, _toConsumableArray(args));
|
|
37
|
-
if (!(0,
|
|
36
|
+
if (!(0, _lodash["default"])(res)) {
|
|
38
37
|
throw new TypeError("`".concat(name, "` prehook must return a boolean value."));
|
|
39
38
|
}
|
|
40
39
|
return res;
|
|
@@ -75,12 +74,12 @@ var useStore = function useStore(prehooks, value, storage) {
|
|
|
75
74
|
var original = _storage.clone(_storage.getItem(storageKey.current));
|
|
76
75
|
var resetData = !propertyPaths.length ? {} : propertyPaths.includes(_constants.FULL_STATE_SELECTOR) ? original : (0, _utils.mapPathsToObject)(original, propertyPaths);
|
|
77
76
|
runPrehook(prehooksRef.current, 'resetState', [resetData, {
|
|
78
|
-
current: (0,
|
|
77
|
+
current: (0, _utils.clonedeep)(state),
|
|
79
78
|
original: original
|
|
80
79
|
}]) && deps.setState(state, resetData, onChange);
|
|
81
80
|
}, []);
|
|
82
81
|
var setState = (0, _react.useCallback)(function (changes) {
|
|
83
|
-
changes = (0,
|
|
82
|
+
changes = (0, _utils.clonedeep)(changes);
|
|
84
83
|
runPrehook(prehooksRef.current, 'setState', [changes]) && deps.setState(state, changes, onChange);
|
|
85
84
|
}, []);
|
|
86
85
|
var subscribe = (0, _react.useCallback)(function (listener) {
|
|
@@ -101,7 +100,7 @@ var useStore = function useStore(prehooks, value, storage) {
|
|
|
101
100
|
mounted.current = true;
|
|
102
101
|
return;
|
|
103
102
|
}
|
|
104
|
-
setState((0,
|
|
103
|
+
setState((0, _utils.clonedeep)(value));
|
|
105
104
|
}, [value]);
|
|
106
105
|
(0, _react.useEffect)(function () {
|
|
107
106
|
if (!listeners.size) {
|
package/dist/main/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @param {ObservableContext<T>} context Refers to the PublicObservableContext<T> type of the ObservableContext<T>
|
|
3
|
-
* @param {
|
|
3
|
+
* @param {SelectorMap<T>} [selectorMap] Key:value pairs where `key` => arbitrary key given to a Store.data property holding a state slice and `value` => property path to a state slice used by this component: see examples below. May add a mapping for a certain arbitrary key='state' and value='@@STATE' to indicate a desire to obtain the entire state object and assign to a `state` property of Store.data. A change in any of the referenced properties results in this component render. When using '@@STATE', note that any change within the state object will result in this component render.
|
|
4
4
|
* @returns {(WrappedComponent: C) => MemoExoticComponent<P>}
|
|
5
5
|
* @template {State} T
|
|
6
6
|
* @template {PartialStore<T> & {[x:string]:*}} [P=PartialStore<T>]
|
|
@@ -10,9 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
export function connect<T extends import("../types").State, P extends PartialStore<T> & {
|
|
12
12
|
[x: string]: any;
|
|
13
|
-
} = PartialStore<T>, C extends ComponentType<P> | import("react").ExoticComponent<ComponentType<P>>>(context: ObservableContext<T>, selectorMap?:
|
|
14
|
-
[selectorKey: string]: string | keyof T;
|
|
15
|
-
}): (WrappedComponent: C) => MemoExoticComponent<P>;
|
|
13
|
+
} = PartialStore<T>, C extends ComponentType<P> | import("react").ExoticComponent<ComponentType<P>>>(context: ObservableContext<T>, selectorMap?: SelectorMap<T>): (WrappedComponent: C) => MemoExoticComponent<P>;
|
|
16
14
|
|
|
17
15
|
/**
|
|
18
16
|
* @returns {ObservableContext<T>} Refers to the IObservableContext<T> type of the ObservableContext<T>
|
|
@@ -27,7 +25,7 @@ export class UsageError extends Error {
|
|
|
27
25
|
* Actively monitors the store and triggers component re-render if any of the watched keys in the state objects changes
|
|
28
26
|
*
|
|
29
27
|
* @param {ObservableContext<T>} context Refers to the PublicObservableContext<T> type of the ObservableContext<T>
|
|
30
|
-
* @param {
|
|
28
|
+
* @param {SelectorMap<T>} [selectorMap = {}] Key:value pairs where `key` => arbitrary key given to a Store.data property holding a state slice and `value` => property path to a state slice used by this component: see examples below. May add a mapping for a certain arbitrary key='state' and value='@@STATE' to indicate a desire to obtain the entire state object and assign to a `state` property of Store.data. A change in any of the referenced properties results in this component render. When using '@@STATE', note that any change within the state object will result in this component render.
|
|
31
29
|
* @returns {Store<T>}
|
|
32
30
|
* @template {State} T
|
|
33
31
|
* @see {ObservableContext<T>}
|
|
@@ -48,9 +46,7 @@ export class UsageError extends Error {
|
|
|
48
46
|
* {myX: 'd.e.f[1].x'} or {myX: 'd.e.f.1.x'} => {myX: 7} // same applies to {myY: 'd.e.f[1].y'} = {myY: 8}; {myZ: 'd.e.f[1].z'} = {myZ: 9}
|
|
49
47
|
* {myData: '@@STATE'} => {myData: state}
|
|
50
48
|
*/
|
|
51
|
-
export function useContext<T extends import("../types").State>(context: ObservableContext<T>, selectorMap?:
|
|
52
|
-
[selectorKey: string]: string | keyof T;
|
|
53
|
-
}): Store<T>;
|
|
49
|
+
export function useContext<T extends import("../types").State>(context: ObservableContext<T>, selectorMap?: SelectorMap<T>): Store<T>;
|
|
54
50
|
export type ObservableContext<T extends import("../types").State> = IObservableContext<T> | PublicObservableContext<T>;
|
|
55
51
|
export type PublicObservableContext<T extends import("../types").State> = WithObservableProvider<Context<Store<T>>, T>;
|
|
56
52
|
export type IObservableContext<T extends import("../types").State> = WithObservableProvider<Context<IStore>, T>;
|
|
@@ -66,11 +62,16 @@ export type ObservableProvider<T extends import("../types").State> = FC<{
|
|
|
66
62
|
export type State = import("../types").State;
|
|
67
63
|
export type PartialState<T extends import("../types").State> = import("../types").PartialState<T>;
|
|
68
64
|
export type Prehooks<T extends import("../types").State> = import("../types").Prehooks<T>;
|
|
65
|
+
export type SelectorMap<T extends import("../types").State> = {
|
|
66
|
+
[dataPropKey: string]: string | keyof T;
|
|
67
|
+
} & {
|
|
68
|
+
[dataPropKey: string]: "@@STATE";
|
|
69
|
+
};
|
|
69
70
|
export type StoreInternal<T extends import("../types").State> = import("../types").StoreInternal<T>;
|
|
70
71
|
export type PartialStore<T extends import("../types").State> = {
|
|
71
72
|
data?: import("../types").Data;
|
|
72
73
|
resetState?: (propertyPaths?: string[]) => void;
|
|
73
|
-
setState?: (changes: import("../types").PartialState<T
|
|
74
|
+
setState?: (changes: import("../types").UpdatePayload<import("../types").PartialState<T>>) => void;
|
|
74
75
|
};
|
|
75
76
|
export type Store<T extends import("../types").State> = import("../types").Store<T>;
|
|
76
77
|
export type IStore = import("../types").IStore;
|