@webkrafters/react-observable-context 4.0.0-rc.0 → 4.0.0-rc.2

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 +79 -63
  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,21 @@ The React-Observable-Context module contains **4** exports namely:
245
249
 
246
250
  # Usage
247
251
 
248
- ### <u>*context.js*</u>
249
-
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;
252
+ <i><b><u>context.js</u></b></i>
253
+ ```
254
+ import { createContext } from '@webkrafters/react-observable-context';
255
255
 
256
- ### <u>*ui.js*</u>
256
+ export default createContext();
257
257
  ```
258
- /********************************************/
259
- /* ui.js: using the `connect` HOC method. */
260
- /********************************************/
261
258
 
259
+ <i><b><u>ui.js</u></b> (connect method)</i>
260
+ ```
262
261
  import React, { useCallback, useEffect } from 'react';
263
- import ObservableContext, { connnectObservableContext } from './context';
264
-
265
- const withConnector = connectObservableContext({ year: 'a.b.x.y.z[0]' });
266
-
267
- const Client1 = withConnector(({ data }) => ( <div>Year: { data.year }</div> ));
262
+ import { connect } from '@webkrafters/react-observable-context';
263
+ import ObservableContext from './context';
268
264
 
269
- const Client2 = withConnector(({ data, setState, resetState }) => {
265
+ export const YearText = ({ data }) => ( <div>Year: { data.year }</div> );
266
+ export const YearInput = ({ data, setState, resetState }) => {
270
267
  const onChange = useCallback( e => setState({
271
268
  a: { b: { x: { y: { z: { 0: e.target.value } } } } }
272
269
  }), [ setState ]);
@@ -274,7 +271,11 @@ const Client2 = withConnector(({ data, setState, resetState }) => {
274
271
  data.year > 2049 && resetState([ 'a.b.c' ]);
275
272
  }, [ data.year ]);
276
273
  return ( <div>Year: <input type="number" onChange={ onChange } /> );
277
- });
274
+ };
275
+
276
+ const withConnector = connect( ObservablContext, { year: 'a.b.x.y.z[0]' } );
277
+ const Client1 = withConnector( YearText );
278
+ const Client2 = withConnector( YearInput );
278
279
 
279
280
  const Ui = () => (
280
281
  <div>
@@ -285,23 +286,22 @@ const Ui = () => (
285
286
 
286
287
  export default Ui;
287
288
  ```
288
- ```
289
- /************************************************/
290
- /* ui.js: using the `useContext` hook method. */
291
- /************************************************/
292
289
 
290
+ <i><b><u>ui.js</u></b> (useContext with memo method)</i>
291
+ ```
293
292
  import React, { memo, useCallback, useEffect } from 'react';
294
- import ObservableContext, { useObservableContext } from './context';
293
+ import { useContext } from '@webkrafters/react-observable-context';
294
+ import ObservableContext from './context';
295
295
 
296
296
  const selectorMap = { year: 'a.b.x.y.z[0]' };
297
297
 
298
298
  const Client1 = memo(() => { // memoize to prevent 'no-change' renders from the parent.
299
- const { data } = useObservableContext( selectorMap );
299
+ const { data } = useContext( ObservableContext, selectorMap );
300
300
  return ( <div>Year: { data.year }</div> );
301
301
  });
302
302
 
303
303
  const Client2 = memo(() => { // memoize to prevent 'no-change' renders from the parent.
304
- const { data, setState, resetState } = useObservableContext( selectorMap );
304
+ const { data, setState, resetState } = useContext( ObservableContext, selectorMap );
305
305
  const onChange = useCallback( e => setState({
306
306
  a: { b: { x: { y: { z: { 0: e.target.value } } } } }
307
307
  }), [ setState ]);
@@ -321,51 +321,67 @@ const Ui = () => (
321
321
  export default Ui;
322
322
  ```
323
323
 
324
- ### <u>*provider.js*</u>
324
+ <i id="provider-usage"><b><u>provider.js</u></b></i>
325
+ ```
326
+ import React, { useCallback, useMemo, useState } from 'react';
327
+ import ObservableContext from './context';
328
+ import Ui from './ui';
329
+
330
+ const initialState = { a: { b: { c: 25, x: { y: { z: [ 2022 ] } } } } };
331
+
332
+ const createStorageStub = data => ({
333
+ clone( data ) { return <your clone function>( data ) },
334
+ data,
335
+ getItem( key ) { return this.data },
336
+ removeItem( key ) {},
337
+ setItem( key, data ) {}
338
+ });
325
339
 
326
- import React, { useCallback, useState } from 'react';
327
- import ObservableContext from './context';
328
- import Ui from './ui';
340
+ const updateHooks = {
341
+ resetState: ( ...args ) => {
342
+ console.log( 'resetting state with >>>> ', JSON.stringify( args ) );
343
+ return true;
344
+ },
345
+ setState: ( ...args ) => {
346
+ console.log( 'merging following into state >>>> ', JSON.stringify( args ) );
347
+ return true;
348
+ }
349
+ };
329
350
 
330
- const initialState = { a: { b: { c: 25, x: { y: { z: [ 2022 ] } } } } };
351
+ const Provider = ({ c = initialState.c }) => {
331
352
 
332
- const storage = {
333
- clone: data => ({ ...data }),
334
- getItem: key => initialState,
335
- removeItem ( key ) {},
336
- setItem ( key, data ) {}
337
- };
353
+ const storage = useMemo(() => createStorageStub({ ...initialsState, c }), []);
338
354
 
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
- };
355
+ const [ state, setState ] = useState(() => storage.getItem());
356
+
357
+ useEffect(() => {
358
+ setState({ c }); // use this (similar to `store.setState`) to update only the changed slice of the context internal state.
359
+ // Do not do this: `setState({ ...state, c });` // it will override the context internal state.
360
+ }, [ c ]);
349
361
 
350
- const Provider = () => (
362
+ return (
351
363
  <ObservableContext.Provider
352
364
  prehooks={ updateHooks }
353
365
  storage={ storage }
354
- value={ initialState }
366
+ value={ state }
355
367
  >
356
368
  <Client />
357
369
  </ObservableContext.Provider>
358
370
  );
359
- Provider.displayName = 'Provider';
360
-
361
- export default Provider;
371
+ };
372
+ Provider.displayName = 'Provider';
373
+
374
+ export default Provider;
375
+ ```
362
376
 
363
- ### <u>*index.js*</u>
377
+ <i><b><u>index.js</u></b></i>
378
+ ```
379
+ import React from 'react';
380
+ import ReactDOM from 'react-dom';
381
+ import Provider from './provider';
364
382
 
365
- import React from 'react';
366
- import ReactDOM from 'react-dom';
367
- import Provider from './provider';
368
- ReactDOM.render(<Provider />, document.getElementById('root'));
383
+ ReactDOM.render( <Provider />, document.getElementById( 'root' ) );
384
+ ```
369
385
 
370
386
  <h1 id="changes">What's Changed?</h1>
371
387
  <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.2"
133
133
  }