@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 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
- a: 1,
116
- b: 2,
117
- c: 3,
118
- d: {
119
- e: 5,
120
- f: [ 6, {
121
- x: 7,
122
- y: 8,
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
- all: '@@STATE',
130
- myData: 'd',
131
- secondFElement: 'd.f[1]'
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
- all: state,
136
- myData: state.d,
137
- secondFElement: state.d.f[1]
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><u>context.js</u></b></i>
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><u>ui.js</u></b> (connect method)</i>
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
- const onChange = useCallback( e => setState({
270
- a: { b: { x: { y: { z: { 0: e.target.value } } } } }
271
- }), [ setState ]);
272
- useEffect(() => {
273
- data.year > 2049 && resetState([ 'a.b.c' ]);
274
- }, [ data.year ]);
275
- return ( <div>Year: <input type="number" onChange={ onChange } /> );
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
- <div>
284
- <Client1 />
285
- <Client2 />
286
- </div>
470
+ <div>
471
+ <Client1 />
472
+ <Client2 />
473
+ </div>
287
474
  );
288
475
 
289
476
  export default Ui;
290
477
  ```
291
478
 
292
- <i><b><u>ui.js</u></b> (useContext with memo method)</i>
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
- const { data } = useContext( ObservableContext, selectorMap );
302
- return ( <div>Year: { data.year }</div> );
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
- const { data, setState, resetState } = useContext( ObservableContext, selectorMap );
307
- const onChange = useCallback( e => setState({
308
- a: { b: { x: { y: { z: { 0: e.target.value } } } } }
309
- }), [ setState ]);
310
- useEffect(() => {
311
- data.year > 2049 && resetState([ 'a.b.c' ]);
312
- }, [ data.year ]);
313
- return ( <div>Year: <input type="number" onChange={ onChange } /> );
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
- <div>
318
- <Client1 />
319
- <Client2 />
320
- </div>
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
- resetState: ( ...args ) => {
336
- console.log( 'resetting state with >>>> ', JSON.stringify( args ) );
337
- return true;
338
- },
339
- setState: ( ...args ) => {
340
- console.log( 'merging following into state >>>> ', JSON.stringify( args ) );
341
- return true;
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
- clone( data ) { return <your clone function>( data ) },
347
- data: null,
348
- getItem( key ) { return this.data },
349
- removeItem( key ) { this.data = null },
350
- setItem( key, data ) { this.data = data }
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
- const [ state, setState ] = useState(() => ({
356
- a: { b: { c, x: { y: { z: [ 2022 ] } } } });
357
- }));
358
-
359
- useEffect(() => {
360
- // similar to `store.setState`, use the following to update
361
- // only the changed slice of the context internal state.
362
- setState({ a: { b: { c } } });
363
- // Do not do the following: it will override the context internal state.
364
- // setState({ ...state, a: { ...state.a, b: { ...state.a.b, c } } });
365
- }, [ c ]);
366
-
367
- return (
368
- <ObservableContext.Provider
369
- prehooks={ updateHooks }
370
- storage={ storageStub }
371
- value={ state }
372
- >
373
- <Ui />
374
- </ObservableContext.Provider>
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>
@@ -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 NULL_STATE_SELECTOR: "";
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 _lodash = _interopRequireDefault(require("lodash.clonedeep"));
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, _lodash["default"])(initStateValue);
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>) => void;
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.clonedeep"));
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, _lodash2["default"])(res)) {
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, _lodash["default"])(state),
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, _lodash["default"])(changes);
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, _lodash["default"])(value));
103
+ setState((0, _utils.clonedeep)(value));
105
104
  }, [value]);
106
105
  (0, _react.useEffect)(function () {
107
106
  if (!listeners.size) {
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @param {ObservableContext<T>} context Refers to the PublicObservableContext<T> type of the ObservableContext<T>
3
- * @param {{[selectorKey: string]: string|keyof 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.
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 {{[selectorKey: string]: string|keyof 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.
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>) => void;
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;