@webkrafters/react-observable-context 4.0.0-rc.0 → 4.0.0-rc.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.
Files changed (2) hide show
  1. package/README.md +78 -58
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -29,13 +29,14 @@ Subscribing component decides which context state properties' changes to trigger
29
29
 
30
30
  **Usage:** Please see [Usage](#usage) section
31
31
 
32
- **Demo:** [Play with the app on codesandbox](https://codesandbox.io/s/github/webKrafters/react-observable-context-app)
32
+ **Demo:** [Play with the app on codesandbox](https://codesandbox.io/s/github/webKrafters/react-observable-context-app)\
33
+ If sandbox fails to load app, please refresh dependencies on its lower left.
33
34
 
34
35
  **Install:**\
35
36
  npm i -S @webkrafters/react-observable-context\
36
37
  npm install --save @webkrafters/react-observable-context
37
38
 
38
- May also see [**What's Changed?**](#changes) section below.
39
+ May also see <b><a href="#changes">What's Changed?</a></b> section below.
39
40
 
40
41
  # Intro
41
42
 
@@ -89,15 +90,18 @@ The property path `a.c.e` accesses the `e=5` property.<br />
89
90
  Either of the property paths `a.c.f.1` and `a.c.f[1]` accesses the `[1]=2` property.<br />
90
91
  A special property path [@@STATE](#fullstate-selectorkey) may be used to access the full given object.<br />
91
92
 
92
- <strong id="fullstate-selectorkey"><u>@@STATE</u></strong> is a special property path to access the full state object as a single slice. ***Caution:*** When this property path exists in a selector map, any change in the state object results in an update of its `store.data` and a subsequent render of its client(s).
93
+ <strong id="fullstate-selectorkey"><u>@@STATE</u></strong> is a special property path to access the full state object as a single slice.<br />
94
+ ***Caution:*** When this property path exists in a <a href="#selector-map">selector map</a>, any change in the state object results in an update of its <a href="#store"><code>store.data</code></a> and a subsequent render of its client(s).
93
95
 
94
96
  ## Provider
95
97
  The Provider component is a property of the `React-Observable-Context` context object. As a `React.context` based provider, it accepts the customary `children` and `value` props. It also accepts **2** optional props: <a href="#prehooks"><code>prehooks</code></a> and <a href="#storage"><code>storage</code></a>.
96
98
 
99
+ Routinely, the `value` prop is initialized with the full initial state. It may only be updated with parts of the state which are changing. Please see a [Provider Usage](#provider-usage) sample below.
100
+
97
101
  <h2 id="selector-map">Selector Map</h2>
98
102
  A selector map is an object holding key:value pairs.<br />
99
- <span style="margin-right: 10px">-</span><code>key</code> refers to an arbitrary name to be assigned to a given property in the <code>store.data</code>.<br />
100
- <span style="margin-right: 10px">-</span><code>value</code> refers to the <a href="#property-path">property path</a> leading to a state slice whose value will be assigned to and observed by this <code>store.data</code> property.<br />
103
+ <span style="margin-right: 10px">-</span><code>key</code> refers to an arbitrary name to be assigned to a given property in the <a href="#store"><code>store.data</code></a>.<br />
104
+ <span style="margin-right: 10px">-</span><code>value</code> refers to the <a href="#property-path">property path</a> leading to a state slice whose value will be assigned to and observed by this <a href="#store"><code>store.data</code></a> property.<br />
101
105
  <span style="margin-right: 10px">-</span>A special '<a href="#fullstate-selectorkey">@@STATE</a>' value may be used to access and observe the full state object.<br />
102
106
 
103
107
  <strong id="selector-map-example">Example:</strong>
@@ -132,7 +136,7 @@ store.data = {
132
136
  ```
133
137
 
134
138
  ## Storage
135
- The `React.Observable.Context` context allows for a user-defined Storage object to maintain the integrity of the initial context state at a location of the user's choosing. This, it accepts, via its Provider's `storage` optional prop. The context defaults to the `window.sessionstorage` in supporting environments. Otherwise, it defaults to its own internal memory-based storage.
139
+ The `React.Observable.Context` context allows for a user-defined Storage object to be provided for maintaining the integrity of the initial context state at a location of the user's choosing. This, it accepts, via its Provider's `storage` optional prop. The context defaults to `window.sessionstorage` in supporting environments. Otherwise, it defaults to its own internal memory-based storage.
136
140
 
137
141
  A valid storage object is of the type: `IStorage<State>` implementing the following **4** methods:
138
142
  <ol>
@@ -143,7 +147,7 @@ A valid storage object is of the type: `IStorage<State>` implementing the follow
143
147
  </ol>
144
148
 
145
149
  ## Store
146
- The `React.Observable.Context` context `store` is the client's facade to the context's underlying state. It exposes **3** properties namely:
150
+ The `React.Observable.Context` context `store` is the client's portal into the context's underlying state. It exposes **3** properties namely:
147
151
  <ol>
148
152
  <li>
149
153
  <p style="margin: 0 0 0 10px">
@@ -245,28 +249,26 @@ The React-Observable-Context module contains **4** exports namely:
245
249
 
246
250
  # Usage
247
251
 
248
- ### <u>*context.js*</u>
252
+ <i><b><u>context.js</u></b></i>
253
+ ```
254
+ import { connect, createContext, useContext } from '@webkrafters/react-observable-context';
255
+
256
+ const ObservableContext = createContext();
257
+
258
+ export const connectObservableContext = selectorMap => connect( ObservablContext, selectorMap );
249
259
 
250
- import { connect, createContext, useContext } from '@webkrafters/react-observable-context';
251
- const ObservableContext = createContext();
252
- export const connectObservableContext = selectorMap => connect( ObservablContext, selectorMap );
253
- export const useObservableContext = selectorMap => useContext( ObservableContext, selectorMap );
254
- export default ObservableContext;
260
+ export const useObservableContext = selectorMap => useContext( ObservableContext, selectorMap );
255
261
 
256
- ### <u>*ui.js*</u>
262
+ export default ObservableContext;
257
263
  ```
258
- /********************************************/
259
- /* ui.js: using the `connect` HOC method. */
260
- /********************************************/
261
264
 
265
+ <i><b><u>ui.js</u></b> (connect method)</i>
266
+ ```
262
267
  import React, { useCallback, useEffect } from 'react';
263
268
  import ObservableContext, { connnectObservableContext } from './context';
264
269
 
265
- const withConnector = connectObservableContext({ year: 'a.b.x.y.z[0]' });
266
-
267
- const Client1 = withConnector(({ data }) => ( <div>Year: { data.year }</div> ));
268
-
269
- const Client2 = withConnector(({ data, setState, resetState }) => {
270
+ export const YearText = ({ data }) => ( <div>Year: { data.year }</div> );
271
+ export const YearInput = ({ data, setState, resetState }) => {
270
272
  const onChange = useCallback( e => setState({
271
273
  a: { b: { x: { y: { z: { 0: e.target.value } } } } }
272
274
  }), [ setState ]);
@@ -274,7 +276,11 @@ const Client2 = withConnector(({ data, setState, resetState }) => {
274
276
  data.year > 2049 && resetState([ 'a.b.c' ]);
275
277
  }, [ data.year ]);
276
278
  return ( <div>Year: <input type="number" onChange={ onChange } /> );
277
- });
279
+ };
280
+
281
+ const withConnector = connectObservableContext({ year: 'a.b.x.y.z[0]' });
282
+ const Client1 = withConnector( YearText );
283
+ const Client2 = withConnector( YearInput );
278
284
 
279
285
  const Ui = () => (
280
286
  <div>
@@ -285,11 +291,9 @@ const Ui = () => (
285
291
 
286
292
  export default Ui;
287
293
  ```
288
- ```
289
- /************************************************/
290
- /* ui.js: using the `useContext` hook method. */
291
- /************************************************/
292
294
 
295
+ <i><b><u>ui.js</u></b> (useContext with memo method)</i>
296
+ ```
293
297
  import React, { memo, useCallback, useEffect } from 'react';
294
298
  import ObservableContext, { useObservableContext } from './context';
295
299
 
@@ -321,51 +325,67 @@ const Ui = () => (
321
325
  export default Ui;
322
326
  ```
323
327
 
324
- ### <u>*provider.js*</u>
328
+ <i id="provider-usage"><b><u>provider.js</u></b></i>
329
+ ```
330
+ import React, { useCallback, useMemo, useState } from 'react';
331
+ import ObservableContext from './context';
332
+ import Ui from './ui';
333
+
334
+ const initialState = { a: { b: { c: 25, x: { y: { z: [ 2022 ] } } } } };
335
+
336
+ const createStorageStub = data => ({
337
+ clone( data ) { return <your clone function>( data ) },
338
+ data,
339
+ getItem( key ) { return this.data },
340
+ removeItem( key ) {},
341
+ setItem( key, data ) {}
342
+ });
325
343
 
326
- import React, { useCallback, useState } from 'react';
327
- import ObservableContext from './context';
328
- import Ui from './ui';
344
+ const updateHooks = {
345
+ resetState: ( ...args ) => {
346
+ console.log( 'resetting state with >>>> ', JSON.stringify( args ) );
347
+ return true;
348
+ },
349
+ setState: ( ...args ) => {
350
+ console.log( 'merging following into state >>>> ', JSON.stringify( args ) );
351
+ return true;
352
+ }
353
+ };
329
354
 
330
- const initialState = { a: { b: { c: 25, x: { y: { z: [ 2022 ] } } } } };
355
+ const Provider = ({ c = initialState.c }) => {
331
356
 
332
- const storage = {
333
- clone: data => ({ ...data }),
334
- getItem: key => initialState,
335
- removeItem ( key ) {},
336
- setItem ( key, data ) {}
337
- };
357
+ const storage = useMemo(() => createStorageStub({ ...initialsState, c }), []);
338
358
 
339
- const updateHooks = {
340
- resetState: ( ...args ) => {
341
- console.log( 'resetting state with >>>> ', JSON.stringify( args ) );
342
- return true;
343
- },
344
- setState: ( ...args ) => {
345
- console.log( 'merging following into state >>>> ', JSON.stringify( args ) );
346
- return true;
347
- }
348
- };
359
+ const [ state, setState ] = useState(() => storage.getItem());
360
+
361
+ useEffect(() => {
362
+ setState({ c }); // use this (similar to `store.setState`) to update only the changed slice of the context internal state.
363
+ // Do not do this: `setState({ ...state, c });` // it will override the context internal state.
364
+ }, [ c ]);
349
365
 
350
- const Provider = () => (
366
+ return (
351
367
  <ObservableContext.Provider
352
368
  prehooks={ updateHooks }
353
369
  storage={ storage }
354
- value={ initialState }
370
+ value={ state }
355
371
  >
356
372
  <Client />
357
373
  </ObservableContext.Provider>
358
374
  );
359
- Provider.displayName = 'Provider';
360
-
361
- export default Provider;
375
+ };
376
+ Provider.displayName = 'Provider';
377
+
378
+ export default Provider;
379
+ ```
362
380
 
363
- ### <u>*index.js*</u>
381
+ <i><b><u>index.js</u></b></i>
382
+ ```
383
+ import React from 'react';
384
+ import ReactDOM from 'react-dom';
385
+ import Provider from './provider';
364
386
 
365
- import React from 'react';
366
- import ReactDOM from 'react-dom';
367
- import Provider from './provider';
368
- ReactDOM.render(<Provider />, document.getElementById('root'));
387
+ ReactDOM.render( <Provider />, document.getElementById( 'root' ) );
388
+ ```
369
389
 
370
390
  <h1 id="changes">What's Changed?</h1>
371
391
  <table>
package/package.json CHANGED
@@ -129,5 +129,5 @@
129
129
  "test:watch": "eslint --fix && jest --updateSnapshot --watchAll"
130
130
  },
131
131
  "types": "dist/main/index.d.ts",
132
- "version": "4.0.0-rc.0"
132
+ "version": "4.0.0-rc.1"
133
133
  }