react-hooks-global-states 16.0.4 → 16.0.5-beta.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/GlobalStore.cjs +1 -1
- package/GlobalStore.d.ts +3 -7
- package/GlobalStore.js +1 -1
- package/GlobalStore.mjs +1 -1
- package/README.md +82 -117
- package/package.json +9 -3
- package/rgsh.mjs +2 -0
- package/types.d.ts +32 -43
package/GlobalStore.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var z=Object.create;var m=Object.defineProperty,D=Object.defineProperties,K=Object.getOwnPropertyDescriptor,N=Object.getOwnPropertyDescriptors,$=Object.getOwnPropertyNames,E=Object.getOwnPropertySymbols,J=Object.getPrototypeOf,_=Object.prototype.hasOwnProperty,Q=Object.prototype.propertyIsEnumerable;var H=(
|
|
1
|
+
"use strict";var z=Object.create;var m=Object.defineProperty,D=Object.defineProperties,K=Object.getOwnPropertyDescriptor,N=Object.getOwnPropertyDescriptors,$=Object.getOwnPropertyNames,E=Object.getOwnPropertySymbols,J=Object.getPrototypeOf,_=Object.prototype.hasOwnProperty,Q=Object.prototype.propertyIsEnumerable;var H=(o,t,e)=>t in o?m(o,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):o[t]=e,g=(o,t)=>{for(var e in t||(t={}))_.call(t,e)&&H(o,e,t[e]);if(E)for(var e of E(t))Q.call(t,e)&&H(o,e,t[e]);return o},O=(o,t)=>D(o,N(t));var X=(o,t)=>{for(var e in t)m(o,e,{get:t[e],enumerable:!0})},U=(o,t,e,a)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of $(t))!_.call(o,s)&&s!==e&&m(o,s,{get:()=>t[s],enumerable:!(a=K(t,s))||a.enumerable});return o};var M=(o,t,e)=>(e=o!=null?z(J(o)):{},U(t||!o||!o.__esModule?m(e,"default",{value:o,enumerable:!0}):e,o)),Y=o=>U(m({},"__esModule",{value:!0}),o);var tt={};X(tt,{GlobalStore:()=>C,createObservable:()=>y,createSelectorHook:()=>R,default:()=>Z});module.exports=Y(tt);var k=require("react"),f=M(require("json-storage-formatter/isFunction")),q=M(require("./isRecord.cjs")),A=M(require("./shallowCompare.cjs")),j=M(require("./throwWrongKeyOnActionCollectionConfig.cjs")),v=M(require("./uniqueId.cjs")),T=M(require("./GlobalStore.debugProps.cjs"));var C=class o{constructor(t,e={metadata:{}}){this.cleanupFunctions=[];this.actionsConfig=null;this.callbacks=null;this.actions=null;this.subscribers=new Set;this.createSelectorHook=R;this.createObservable=y;var r,l,u;this.dispose=this.dispose.bind(this),this.getMetadata=this.getMetadata.bind(this),this.getState=this.getState.bind(this),this.reset=this.reset.bind(this),this.setMetadata=this.setMetadata.bind(this),this.setState=this.setState.bind(this),this.subscribe=this.subscribe.bind(this);let{metadata:a,callbacks:s,actions:i,name:c}=e;if((0,f.default)(t)&&(this.stateCallback=t),(0,f.default)(a)&&(this.metadataCallback=a),this._name=c!=null?c:(0,v.default)("gs:"),this.state=this.stateCallback?this.stateCallback():t,this.metadata=this.metadataCallback?this.metadataCallback():a!=null?a:{},this.callbacks=s!=null?s:null,this.actionsConfig=i!=null?i:null,T.default.isDevToolsPresent){let b=(r=new Error().stack)!=null?r:"";(u=(l=T.default).REACT_GLOBAL_STATE_HOOK_DEBUG)==null||u.call(l,this,e,b)}this.constructor===o&&this.initialize()}async initialize(){var r,l,u,b,d,S,h,p;let t=(l=(r=this.__devtools_initialize_getStoreActionsMapWrapped)==null?void 0:r.call(this))!=null?l:this.getStoreActionsMap(),{actions:e,storeTools:a}=t;this.actions=e,this.storeTools=a,this.use=this.getMainHook();let s=(b=(u=this.onInit)==null?void 0:u.call(this))!=null?b:null;(0,f.default)(s)&&this.cleanupFunctions.push(s);let{onInit:i}=(d=this.callbacks)!=null?d:{};if(!i)return;let c=(h=(S=this.__devtools_getLifeCycleStoreToolsWrapper)==null?void 0:S.call(this,"config/onInit/"))!=null?h:a,n=(p=i==null?void 0:i(c))!=null?p:null;(0,f.default)(n)&&this.cleanupFunctions.push(n)}executeSetStateForSubscriber(t,e){let{selector:a,onStoreChange:s,currentState:i,isEqualRoot:c=(l,u)=>l===u,isEqual:n=(l,u)=>l===u}=t;if(!e.forceUpdate&&c(e.currentState,e.newState))return{didUpdate:!1};let r=a?a(e.newState):e.newState;return!e.forceUpdate&&n(i,r)?{didUpdate:!1}:(this.partialUpdateSubscription(t,{currentState:r}),s({state:r},{identifier:e.identifier}),{didUpdate:!0})}setActualStateWithoutValidations(t,{forceUpdate:e,identifier:a}){let s=this.state;if(!(e||s!==t))return;this.state=t;let c=this.subscribers.values(),n={forceUpdate:e,newState:t,currentState:s,identifier:a};for(let r of c)this.executeSetStateForSubscriber(r,n)}setMetadata(t){let e=(0,f.default)(t)?t(this.metadata):t;this.metadata=e}getMetadata(){return this.metadata}getState(){return this.state}subscribe(...[t,e,a]){var u;let s=(0,f.default)(e),i=s?t:void 0,c=s?e:t,n=(u=s?a:e)!=null?u:void 0,r=i?i(this.state):this.state;n!=null&&n.skipFirst||c(r);let l=g({selector:i,currentState:r,onStoreChange:({state:b})=>c(b)},n);return this.subscribeCallback(l),()=>{this.subscribers.delete(l)}}subscribeCallback(t){var e,a;return(a=(e=this.callbacks)==null?void 0:e.onSubscribed)==null||a.call(e,this.storeTools,t),this.subscribers.add(t),()=>{this.subscribers.delete(t)}}partialUpdateSubscription(t,e){Object.assign(t,e)}getMainHook(){let t=`globalHook:${this._name}`,e=(d,S=[])=>{(0,k.useDebugValue)(t);let h=(0,A.isArray)(S)?{dependencies:S}:S!=null?S:{},p=(0,k.useRef)(null),x=P=>(0,f.default)(d)?d(P):P;p.current=p.current?p.current:g({selector:x,currentState:x(this.state),onStoreChange:()=>{throw new Error("Callback not set")}},h);let V=p.current.dependencies,w=h==null?void 0:h.dependencies,W=O(g({},h),{selector:x,dependencies:w});this.partialUpdateSubscription(p.current,W);let[{subscribe:B,getSnapshot:I,getServerSnapshot:L}]=(0,k.useState)(()=>{let P=G=>(p.current.onStoreChange=G,this.subscribeCallback(p.current)),F=()=>p.current.currentState;return{subscribe:P,getSnapshot:F,getServerSnapshot:F}});return this.reselectIfDependenciesChanged({subscription:p.current,newDependencies:w,currentDependencies:V}),[(0,k.useSyncExternalStore)(B,I,L),this.getStateOrchestrator(),this.metadata]},a=this,{getMetadata:s,dispose:i,getState:c,reset:n,setMetadata:r,setState:l,subscribe:u}=this,b={actions:this.actions,createObservable:this.createObservable.bind(a),createSelectorHook:this.createSelectorHook.bind(a),dispose:i,metadata:void 0,getMetadata:s,getState:c,reset:n,setMetadata:r,setState:l,subscribe:u,subscribers:this.subscribers,use:e,select:(...d)=>e(...d)[0]};return Object.assign(e,b),Object.defineProperty(e,"metadata",{configurable:!0,enumerable:!0,get:s}),e}reselectIfDependenciesChanged({subscription:t,newDependencies:e,currentDependencies:a}){!t.selector||a===e||(a==null?void 0:a.length)===(e==null?void 0:e.length)&&(0,A.default)(a,e)||this.partialUpdateSubscription(t,{currentState:t.selector(this.state)})}getStateOrchestrator(){return this.actions?this.actions:this.setState}setState(t,{forceUpdate:e,identifier:a}={}){var l,u,b,d,S,h;let s=this.state,i=(0,f.default)(t)?t(s):t;if(!e&&this.state===i)return;let c=this.setActualStateWithoutValidations,n={previousState:s,state:i,identifier:a},r=O(g(g({},this.storeTools),n),{setState:c});Object.defineProperty(r,"metadata",{configurable:!0,enumerable:!0,get:r.getMetadata}),!((l=this.callbacks)!=null&&l.computePreventStateChange&&((b=(u=this.callbacks)==null?void 0:u.computePreventStateChange)!=null&&b.call(u,r)))&&(this.setActualStateWithoutValidations(i,{forceUpdate:e,identifier:a}),(d=this.onStateChanged)==null||d.call(this,r),(h=(S=this.callbacks)==null?void 0:S.onStateChanged)==null||h.call(S,r))}getStoreActionsMap(){let{getMetadata:t,setMetadata:e,getState:a,setState:s,subscribe:i}=this,c={setMetadata:e,get metadata(){return t()},getMetadata:t,getState:a,setState:s,subscribe:i,actions:null};if(!(0,q.default)(this.actionsConfig))return{actions:null,storeTools:c};let n=this.actionsConfig,r={};c.actions=r;let l=Object.keys(n);for(let u of l)Object.assign(r,{[u](...b){let S=n[u].apply(r,b);return typeof S!="function"&&(0,j.default)(u),S.call(r,c)}});return{actions:r,storeTools:c}}removeSubscriptions(){this.subscribers.clear()}executeCleanupTasks(){this.cleanupFunctions.forEach(t=>{t==null||t()}),this.cleanupFunctions.length=0}dispose(){this.removeSubscriptions(),this.executeCleanupTasks()}reset(...t){var l,u,b,d,S,h;this.executeCleanupTasks();let e=t.length>0,a=e?t[0]:this.stateCallback?this.stateCallback():this.state,s=e?t[1]:this.metadataCallback?this.metadataCallback():this.metadata;this.setActualStateWithoutValidations(a,{forceUpdate:!0}),this.setMetadata(s);let i=(u=(l=this.onInit)==null?void 0:l.call(this))!=null?u:null;(0,f.default)(i)&&this.cleanupFunctions.push(i);let{onInit:c}=(b=this.callbacks)!=null?b:{};if(!c)return;let n=(S=(d=this.__devtools_getLifeCycleStoreToolsWrapper)==null?void 0:d.call(this,"config/onInit/"))!=null?S:this.storeTools,r=(h=c==null?void 0:c(n))!=null?h:null;(0,f.default)(r)&&this.cleanupFunctions.push(r)}};function y(o,t){let e=t==null?void 0:t.name,a=t==null?void 0:t.isEqualRoot,s=t==null?void 0:t.isEqual,i=this.getState(),c=(o!=null?o:b=>b)(i),n=new C(c,{name:e!=null?e:(0,v.default)("sh:")}),r=this.subscribe(b=>{if((a!=null?a:Object.is)(i,b))return;i=b;let S=o(b);(s!=null?s:Object.is)(c,S)||(c=S,n.setState(S))},{skipFirst:!0}),l=n.subscribe.bind(n),u={getState:n.getState.bind(n),subscribe:n.subscribe.bind(n),createSelectorHook:R.bind(l),createObservable:y.bind(l),dispose:()=>{r(),n.dispose()},subscribers:n.subscribers};return Object.assign(l,u),l}function R(o,t){let e=t==null?void 0:t.name,a=t==null?void 0:t.isEqualRoot,s=t==null?void 0:t.isEqual,i=this.getState(),c=(o!=null?o:b=>b)(i),n=new C(c,{name:e!=null?e:(0,v.default)("sh:")}),r=this.subscribe(b=>{if((a!=null?a:Object.is)(i,b))return;i=b;let S=o(b);(s!=null?s:Object.is)(c,S)||(c=S,n.setState(S))},{skipFirst:!0}),l=(...b)=>{let[d]=n.use(...b);return d},u={getState:()=>n.getState(),subscribe:n.subscribe.bind(n),createSelectorHook:R.bind(l),createObservable:y.bind(l),dispose:()=>{r(),n.dispose()},subscribers:n.subscribers};return Object.assign(l,u),l}var Z=C;
|
package/GlobalStore.d.ts
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import type { ActionCollectionConfig, GlobalStoreCallbacks, ActionCollectionResult, MetadataSetter, SubscribeCallbackConfig, SubscribeCallback, SelectorCallback, SubscriberParameters, StateHook, BaseMetadata, StateChanges, StoreTools, ObservableFragment, UnsubscribeCallback, AnyFunction, ReadonlyHook, ReadonlyStateApi, CleanupFunction } from './types';
|
|
2
|
-
/**
|
|
3
|
-
* The GlobalStore class is the main class of the library and it is used to create a GlobalStore instances
|
|
4
|
-
* */
|
|
2
|
+
/** Shared state, subscriptions, actions and lifecycle management. */
|
|
5
3
|
export declare class GlobalStore<State, Metadata extends BaseMetadata, ActionsConfig extends ActionCollectionConfig<State, Metadata> | undefined | unknown, PublicStateMutator = keyof ActionsConfig extends never | undefined ? React.Dispatch<React.SetStateAction<State>> : ActionCollectionResult<State, Metadata, NonNullable<ActionsConfig>>> {
|
|
6
4
|
protected cleanupFunctions: CleanupFunction[];
|
|
7
5
|
protected _name: string;
|
|
@@ -18,9 +16,7 @@ export declare class GlobalStore<State, Metadata extends BaseMetadata, ActionsCo
|
|
|
18
16
|
*/
|
|
19
17
|
use: StateHook<State, PublicStateMutator, Metadata>;
|
|
20
18
|
/**
|
|
21
|
-
* @deprecated
|
|
22
|
-
* @description Set of subscribers that are listening to state changes
|
|
23
|
-
* Useful for debugging purposes... You'll probably not need to use this in your application
|
|
19
|
+
* @deprecated Internal subscription registry. Use `subscribe` to observe state changes.
|
|
24
20
|
*/
|
|
25
21
|
subscribers: Set<SubscriberParameters>;
|
|
26
22
|
state: State;
|
|
@@ -70,7 +66,7 @@ export declare class GlobalStore<State, Metadata extends BaseMetadata, ActionsCo
|
|
|
70
66
|
identifier?: string;
|
|
71
67
|
}): void;
|
|
72
68
|
/**
|
|
73
|
-
* Set the value of the metadata property, this is
|
|
69
|
+
* Set the value of the metadata property, this is non-reactive and will not trigger a re-render
|
|
74
70
|
* @param {MetadataSetter<Metadata>} setter - The setter function or the value to set
|
|
75
71
|
* */
|
|
76
72
|
setMetadata(setter: Parameters<MetadataSetter<Metadata>>[0]): void;
|
package/GlobalStore.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var z=Object.create;var m=Object.defineProperty,D=Object.defineProperties,K=Object.getOwnPropertyDescriptor,N=Object.getOwnPropertyDescriptors,$=Object.getOwnPropertyNames,E=Object.getOwnPropertySymbols,J=Object.getPrototypeOf,_=Object.prototype.hasOwnProperty,Q=Object.prototype.propertyIsEnumerable;var H=(
|
|
1
|
+
"use strict";var z=Object.create;var m=Object.defineProperty,D=Object.defineProperties,K=Object.getOwnPropertyDescriptor,N=Object.getOwnPropertyDescriptors,$=Object.getOwnPropertyNames,E=Object.getOwnPropertySymbols,J=Object.getPrototypeOf,_=Object.prototype.hasOwnProperty,Q=Object.prototype.propertyIsEnumerable;var H=(o,t,e)=>t in o?m(o,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):o[t]=e,g=(o,t)=>{for(var e in t||(t={}))_.call(t,e)&&H(o,e,t[e]);if(E)for(var e of E(t))Q.call(t,e)&&H(o,e,t[e]);return o},O=(o,t)=>D(o,N(t));var X=(o,t)=>{for(var e in t)m(o,e,{get:t[e],enumerable:!0})},U=(o,t,e,a)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of $(t))!_.call(o,s)&&s!==e&&m(o,s,{get:()=>t[s],enumerable:!(a=K(t,s))||a.enumerable});return o};var M=(o,t,e)=>(e=o!=null?z(J(o)):{},U(t||!o||!o.__esModule?m(e,"default",{value:o,enumerable:!0}):e,o)),Y=o=>U(m({},"__esModule",{value:!0}),o);var tt={};X(tt,{GlobalStore:()=>C,createObservable:()=>y,createSelectorHook:()=>R,default:()=>Z});module.exports=Y(tt);var k=require("react"),f=M(require("json-storage-formatter/isFunction")),q=M(require("./isRecord.js")),A=M(require("./shallowCompare.js")),j=M(require("./throwWrongKeyOnActionCollectionConfig.js")),v=M(require("./uniqueId.js")),T=M(require("./GlobalStore.debugProps.js"));var C=class o{constructor(t,e={metadata:{}}){this.cleanupFunctions=[];this.actionsConfig=null;this.callbacks=null;this.actions=null;this.subscribers=new Set;this.createSelectorHook=R;this.createObservable=y;var r,l,u;this.dispose=this.dispose.bind(this),this.getMetadata=this.getMetadata.bind(this),this.getState=this.getState.bind(this),this.reset=this.reset.bind(this),this.setMetadata=this.setMetadata.bind(this),this.setState=this.setState.bind(this),this.subscribe=this.subscribe.bind(this);let{metadata:a,callbacks:s,actions:i,name:c}=e;if((0,f.default)(t)&&(this.stateCallback=t),(0,f.default)(a)&&(this.metadataCallback=a),this._name=c!=null?c:(0,v.default)("gs:"),this.state=this.stateCallback?this.stateCallback():t,this.metadata=this.metadataCallback?this.metadataCallback():a!=null?a:{},this.callbacks=s!=null?s:null,this.actionsConfig=i!=null?i:null,T.default.isDevToolsPresent){let b=(r=new Error().stack)!=null?r:"";(u=(l=T.default).REACT_GLOBAL_STATE_HOOK_DEBUG)==null||u.call(l,this,e,b)}this.constructor===o&&this.initialize()}async initialize(){var r,l,u,b,d,S,h,p;let t=(l=(r=this.__devtools_initialize_getStoreActionsMapWrapped)==null?void 0:r.call(this))!=null?l:this.getStoreActionsMap(),{actions:e,storeTools:a}=t;this.actions=e,this.storeTools=a,this.use=this.getMainHook();let s=(b=(u=this.onInit)==null?void 0:u.call(this))!=null?b:null;(0,f.default)(s)&&this.cleanupFunctions.push(s);let{onInit:i}=(d=this.callbacks)!=null?d:{};if(!i)return;let c=(h=(S=this.__devtools_getLifeCycleStoreToolsWrapper)==null?void 0:S.call(this,"config/onInit/"))!=null?h:a,n=(p=i==null?void 0:i(c))!=null?p:null;(0,f.default)(n)&&this.cleanupFunctions.push(n)}executeSetStateForSubscriber(t,e){let{selector:a,onStoreChange:s,currentState:i,isEqualRoot:c=(l,u)=>l===u,isEqual:n=(l,u)=>l===u}=t;if(!e.forceUpdate&&c(e.currentState,e.newState))return{didUpdate:!1};let r=a?a(e.newState):e.newState;return!e.forceUpdate&&n(i,r)?{didUpdate:!1}:(this.partialUpdateSubscription(t,{currentState:r}),s({state:r},{identifier:e.identifier}),{didUpdate:!0})}setActualStateWithoutValidations(t,{forceUpdate:e,identifier:a}){let s=this.state;if(!(e||s!==t))return;this.state=t;let c=this.subscribers.values(),n={forceUpdate:e,newState:t,currentState:s,identifier:a};for(let r of c)this.executeSetStateForSubscriber(r,n)}setMetadata(t){let e=(0,f.default)(t)?t(this.metadata):t;this.metadata=e}getMetadata(){return this.metadata}getState(){return this.state}subscribe(...[t,e,a]){var u;let s=(0,f.default)(e),i=s?t:void 0,c=s?e:t,n=(u=s?a:e)!=null?u:void 0,r=i?i(this.state):this.state;n!=null&&n.skipFirst||c(r);let l=g({selector:i,currentState:r,onStoreChange:({state:b})=>c(b)},n);return this.subscribeCallback(l),()=>{this.subscribers.delete(l)}}subscribeCallback(t){var e,a;return(a=(e=this.callbacks)==null?void 0:e.onSubscribed)==null||a.call(e,this.storeTools,t),this.subscribers.add(t),()=>{this.subscribers.delete(t)}}partialUpdateSubscription(t,e){Object.assign(t,e)}getMainHook(){let t=`globalHook:${this._name}`,e=(d,S=[])=>{(0,k.useDebugValue)(t);let h=(0,A.isArray)(S)?{dependencies:S}:S!=null?S:{},p=(0,k.useRef)(null),x=P=>(0,f.default)(d)?d(P):P;p.current=p.current?p.current:g({selector:x,currentState:x(this.state),onStoreChange:()=>{throw new Error("Callback not set")}},h);let V=p.current.dependencies,w=h==null?void 0:h.dependencies,W=O(g({},h),{selector:x,dependencies:w});this.partialUpdateSubscription(p.current,W);let[{subscribe:B,getSnapshot:I,getServerSnapshot:L}]=(0,k.useState)(()=>{let P=G=>(p.current.onStoreChange=G,this.subscribeCallback(p.current)),F=()=>p.current.currentState;return{subscribe:P,getSnapshot:F,getServerSnapshot:F}});return this.reselectIfDependenciesChanged({subscription:p.current,newDependencies:w,currentDependencies:V}),[(0,k.useSyncExternalStore)(B,I,L),this.getStateOrchestrator(),this.metadata]},a=this,{getMetadata:s,dispose:i,getState:c,reset:n,setMetadata:r,setState:l,subscribe:u}=this,b={actions:this.actions,createObservable:this.createObservable.bind(a),createSelectorHook:this.createSelectorHook.bind(a),dispose:i,metadata:void 0,getMetadata:s,getState:c,reset:n,setMetadata:r,setState:l,subscribe:u,subscribers:this.subscribers,use:e,select:(...d)=>e(...d)[0]};return Object.assign(e,b),Object.defineProperty(e,"metadata",{configurable:!0,enumerable:!0,get:s}),e}reselectIfDependenciesChanged({subscription:t,newDependencies:e,currentDependencies:a}){!t.selector||a===e||(a==null?void 0:a.length)===(e==null?void 0:e.length)&&(0,A.default)(a,e)||this.partialUpdateSubscription(t,{currentState:t.selector(this.state)})}getStateOrchestrator(){return this.actions?this.actions:this.setState}setState(t,{forceUpdate:e,identifier:a}={}){var l,u,b,d,S,h;let s=this.state,i=(0,f.default)(t)?t(s):t;if(!e&&this.state===i)return;let c=this.setActualStateWithoutValidations,n={previousState:s,state:i,identifier:a},r=O(g(g({},this.storeTools),n),{setState:c});Object.defineProperty(r,"metadata",{configurable:!0,enumerable:!0,get:r.getMetadata}),!((l=this.callbacks)!=null&&l.computePreventStateChange&&((b=(u=this.callbacks)==null?void 0:u.computePreventStateChange)!=null&&b.call(u,r)))&&(this.setActualStateWithoutValidations(i,{forceUpdate:e,identifier:a}),(d=this.onStateChanged)==null||d.call(this,r),(h=(S=this.callbacks)==null?void 0:S.onStateChanged)==null||h.call(S,r))}getStoreActionsMap(){let{getMetadata:t,setMetadata:e,getState:a,setState:s,subscribe:i}=this,c={setMetadata:e,get metadata(){return t()},getMetadata:t,getState:a,setState:s,subscribe:i,actions:null};if(!(0,q.default)(this.actionsConfig))return{actions:null,storeTools:c};let n=this.actionsConfig,r={};c.actions=r;let l=Object.keys(n);for(let u of l)Object.assign(r,{[u](...b){let S=n[u].apply(r,b);return typeof S!="function"&&(0,j.default)(u),S.call(r,c)}});return{actions:r,storeTools:c}}removeSubscriptions(){this.subscribers.clear()}executeCleanupTasks(){this.cleanupFunctions.forEach(t=>{t==null||t()}),this.cleanupFunctions.length=0}dispose(){this.removeSubscriptions(),this.executeCleanupTasks()}reset(...t){var l,u,b,d,S,h;this.executeCleanupTasks();let e=t.length>0,a=e?t[0]:this.stateCallback?this.stateCallback():this.state,s=e?t[1]:this.metadataCallback?this.metadataCallback():this.metadata;this.setActualStateWithoutValidations(a,{forceUpdate:!0}),this.setMetadata(s);let i=(u=(l=this.onInit)==null?void 0:l.call(this))!=null?u:null;(0,f.default)(i)&&this.cleanupFunctions.push(i);let{onInit:c}=(b=this.callbacks)!=null?b:{};if(!c)return;let n=(S=(d=this.__devtools_getLifeCycleStoreToolsWrapper)==null?void 0:d.call(this,"config/onInit/"))!=null?S:this.storeTools,r=(h=c==null?void 0:c(n))!=null?h:null;(0,f.default)(r)&&this.cleanupFunctions.push(r)}};function y(o,t){let e=t==null?void 0:t.name,a=t==null?void 0:t.isEqualRoot,s=t==null?void 0:t.isEqual,i=this.getState(),c=(o!=null?o:b=>b)(i),n=new C(c,{name:e!=null?e:(0,v.default)("sh:")}),r=this.subscribe(b=>{if((a!=null?a:Object.is)(i,b))return;i=b;let S=o(b);(s!=null?s:Object.is)(c,S)||(c=S,n.setState(S))},{skipFirst:!0}),l=n.subscribe.bind(n),u={getState:n.getState.bind(n),subscribe:n.subscribe.bind(n),createSelectorHook:R.bind(l),createObservable:y.bind(l),dispose:()=>{r(),n.dispose()},subscribers:n.subscribers};return Object.assign(l,u),l}function R(o,t){let e=t==null?void 0:t.name,a=t==null?void 0:t.isEqualRoot,s=t==null?void 0:t.isEqual,i=this.getState(),c=(o!=null?o:b=>b)(i),n=new C(c,{name:e!=null?e:(0,v.default)("sh:")}),r=this.subscribe(b=>{if((a!=null?a:Object.is)(i,b))return;i=b;let S=o(b);(s!=null?s:Object.is)(c,S)||(c=S,n.setState(S))},{skipFirst:!0}),l=(...b)=>{let[d]=n.use(...b);return d},u={getState:()=>n.getState(),subscribe:n.subscribe.bind(n),createSelectorHook:R.bind(l),createObservable:y.bind(l),dispose:()=>{r(),n.dispose()},subscribers:n.subscribers};return Object.assign(l,u),l}var Z=C;
|
package/GlobalStore.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var q=Object.defineProperty,j=Object.defineProperties;var V=Object.getOwnPropertyDescriptors;var x=Object.getOwnPropertySymbols;var W=Object.prototype.hasOwnProperty,B=Object.prototype.propertyIsEnumerable;var O=(S,t,e)=>t in S?q(S,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):S[t]=e,k=(S,t)=>{for(var e in t||(t={}))W.call(t,e)&&O(S,e,t[e]);if(x)for(var e of x(t))B.call(t,e)&&O(S,e,t[e]);return S},m=(S,t)=>j(S,V(t));import{useDebugValue as I,
|
|
1
|
+
var q=Object.defineProperty,j=Object.defineProperties;var V=Object.getOwnPropertyDescriptors;var x=Object.getOwnPropertySymbols;var W=Object.prototype.hasOwnProperty,B=Object.prototype.propertyIsEnumerable;var O=(S,t,e)=>t in S?q(S,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):S[t]=e,k=(S,t)=>{for(var e in t||(t={}))W.call(t,e)&&O(S,e,t[e]);if(x)for(var e of x(t))B.call(t,e)&&O(S,e,t[e]);return S},m=(S,t)=>j(S,V(t));import{useDebugValue as I,useRef as L,useState as G,useSyncExternalStore as z}from"react";import f from"json-storage-formatter/isFunction";import D from"./isRecord.mjs";import K,{isArray as N}from"./shallowCompare.mjs";import $ from"./throwWrongKeyOnActionCollectionConfig.mjs";import P from"./uniqueId.mjs";import T from"./GlobalStore.debugProps.mjs";var g=class S{constructor(t,e={metadata:{}}){this.cleanupFunctions=[];this.actionsConfig=null;this.callbacks=null;this.actions=null;this.subscribers=new Set;this.createSelectorHook=v;this.createObservable=A;var o,c,l;this.dispose=this.dispose.bind(this),this.getMetadata=this.getMetadata.bind(this),this.getState=this.getState.bind(this),this.reset=this.reset.bind(this),this.setMetadata=this.setMetadata.bind(this),this.setState=this.setState.bind(this),this.subscribe=this.subscribe.bind(this);let{metadata:a,callbacks:i,actions:n,name:r}=e;if(f(t)&&(this.stateCallback=t),f(a)&&(this.metadataCallback=a),this._name=r!=null?r:P("gs:"),this.state=this.stateCallback?this.stateCallback():t,this.metadata=this.metadataCallback?this.metadataCallback():a!=null?a:{},this.callbacks=i!=null?i:null,this.actionsConfig=n!=null?n:null,T.isDevToolsPresent){let u=(o=new Error().stack)!=null?o:"";(l=(c=T).REACT_GLOBAL_STATE_HOOK_DEBUG)==null||l.call(c,this,e,u)}this.constructor===S&&this.initialize()}async initialize(){var o,c,l,u,d,b,h,p;let t=(c=(o=this.__devtools_initialize_getStoreActionsMapWrapped)==null?void 0:o.call(this))!=null?c:this.getStoreActionsMap(),{actions:e,storeTools:a}=t;this.actions=e,this.storeTools=a,this.use=this.getMainHook();let i=(u=(l=this.onInit)==null?void 0:l.call(this))!=null?u:null;f(i)&&this.cleanupFunctions.push(i);let{onInit:n}=(d=this.callbacks)!=null?d:{};if(!n)return;let r=(h=(b=this.__devtools_getLifeCycleStoreToolsWrapper)==null?void 0:b.call(this,"config/onInit/"))!=null?h:a,s=(p=n==null?void 0:n(r))!=null?p:null;f(s)&&this.cleanupFunctions.push(s)}executeSetStateForSubscriber(t,e){let{selector:a,onStoreChange:i,currentState:n,isEqualRoot:r=(c,l)=>c===l,isEqual:s=(c,l)=>c===l}=t;if(!e.forceUpdate&&r(e.currentState,e.newState))return{didUpdate:!1};let o=a?a(e.newState):e.newState;return!e.forceUpdate&&s(n,o)?{didUpdate:!1}:(this.partialUpdateSubscription(t,{currentState:o}),i({state:o},{identifier:e.identifier}),{didUpdate:!0})}setActualStateWithoutValidations(t,{forceUpdate:e,identifier:a}){let i=this.state;if(!(e||i!==t))return;this.state=t;let r=this.subscribers.values(),s={forceUpdate:e,newState:t,currentState:i,identifier:a};for(let o of r)this.executeSetStateForSubscriber(o,s)}setMetadata(t){let e=f(t)?t(this.metadata):t;this.metadata=e}getMetadata(){return this.metadata}getState(){return this.state}subscribe(...[t,e,a]){var l;let i=f(e),n=i?t:void 0,r=i?e:t,s=(l=i?a:e)!=null?l:void 0,o=n?n(this.state):this.state;s!=null&&s.skipFirst||r(o);let c=k({selector:n,currentState:o,onStoreChange:({state:u})=>r(u)},s);return this.subscribeCallback(c),()=>{this.subscribers.delete(c)}}subscribeCallback(t){var e,a;return(a=(e=this.callbacks)==null?void 0:e.onSubscribed)==null||a.call(e,this.storeTools,t),this.subscribers.add(t),()=>{this.subscribers.delete(t)}}partialUpdateSubscription(t,e){Object.assign(t,e)}getMainHook(){let t=`globalHook:${this._name}`,e=(d,b=[])=>{I(t);let h=N(b)?{dependencies:b}:b!=null?b:{},p=L(null),C=M=>f(d)?d(M):M;p.current=p.current?p.current:k({selector:C,currentState:C(this.state),onStoreChange:()=>{throw new Error("Callback not set")}},h);let w=p.current.dependencies,y=h==null?void 0:h.dependencies,F=m(k({},h),{selector:C,dependencies:y});this.partialUpdateSubscription(p.current,F);let[{subscribe:E,getSnapshot:H,getServerSnapshot:_}]=G(()=>{let M=U=>(p.current.onStoreChange=U,this.subscribeCallback(p.current)),R=()=>p.current.currentState;return{subscribe:M,getSnapshot:R,getServerSnapshot:R}});return this.reselectIfDependenciesChanged({subscription:p.current,newDependencies:y,currentDependencies:w}),[z(E,H,_),this.getStateOrchestrator(),this.metadata]},a=this,{getMetadata:i,dispose:n,getState:r,reset:s,setMetadata:o,setState:c,subscribe:l}=this,u={actions:this.actions,createObservable:this.createObservable.bind(a),createSelectorHook:this.createSelectorHook.bind(a),dispose:n,metadata:void 0,getMetadata:i,getState:r,reset:s,setMetadata:o,setState:c,subscribe:l,subscribers:this.subscribers,use:e,select:(...d)=>e(...d)[0]};return Object.assign(e,u),Object.defineProperty(e,"metadata",{configurable:!0,enumerable:!0,get:i}),e}reselectIfDependenciesChanged({subscription:t,newDependencies:e,currentDependencies:a}){!t.selector||a===e||(a==null?void 0:a.length)===(e==null?void 0:e.length)&&K(a,e)||this.partialUpdateSubscription(t,{currentState:t.selector(this.state)})}getStateOrchestrator(){return this.actions?this.actions:this.setState}setState(t,{forceUpdate:e,identifier:a}={}){var c,l,u,d,b,h;let i=this.state,n=f(t)?t(i):t;if(!e&&this.state===n)return;let r=this.setActualStateWithoutValidations,s={previousState:i,state:n,identifier:a},o=m(k(k({},this.storeTools),s),{setState:r});Object.defineProperty(o,"metadata",{configurable:!0,enumerable:!0,get:o.getMetadata}),!((c=this.callbacks)!=null&&c.computePreventStateChange&&((u=(l=this.callbacks)==null?void 0:l.computePreventStateChange)!=null&&u.call(l,o)))&&(this.setActualStateWithoutValidations(n,{forceUpdate:e,identifier:a}),(d=this.onStateChanged)==null||d.call(this,o),(h=(b=this.callbacks)==null?void 0:b.onStateChanged)==null||h.call(b,o))}getStoreActionsMap(){let{getMetadata:t,setMetadata:e,getState:a,setState:i,subscribe:n}=this,r={setMetadata:e,get metadata(){return t()},getMetadata:t,getState:a,setState:i,subscribe:n,actions:null};if(!D(this.actionsConfig))return{actions:null,storeTools:r};let s=this.actionsConfig,o={};r.actions=o;let c=Object.keys(s);for(let l of c)Object.assign(o,{[l](...u){let b=s[l].apply(o,u);return typeof b!="function"&&$(l),b.call(o,r)}});return{actions:o,storeTools:r}}removeSubscriptions(){this.subscribers.clear()}executeCleanupTasks(){this.cleanupFunctions.forEach(t=>{t==null||t()}),this.cleanupFunctions.length=0}dispose(){this.removeSubscriptions(),this.executeCleanupTasks()}reset(...t){var c,l,u,d,b,h;this.executeCleanupTasks();let e=t.length>0,a=e?t[0]:this.stateCallback?this.stateCallback():this.state,i=e?t[1]:this.metadataCallback?this.metadataCallback():this.metadata;this.setActualStateWithoutValidations(a,{forceUpdate:!0}),this.setMetadata(i);let n=(l=(c=this.onInit)==null?void 0:c.call(this))!=null?l:null;f(n)&&this.cleanupFunctions.push(n);let{onInit:r}=(u=this.callbacks)!=null?u:{};if(!r)return;let s=(b=(d=this.__devtools_getLifeCycleStoreToolsWrapper)==null?void 0:d.call(this,"config/onInit/"))!=null?b:this.storeTools,o=(h=r==null?void 0:r(s))!=null?h:null;f(o)&&this.cleanupFunctions.push(o)}};function A(S,t){let e=t==null?void 0:t.name,a=t==null?void 0:t.isEqualRoot,i=t==null?void 0:t.isEqual,n=this.getState(),r=(S!=null?S:u=>u)(n),s=new g(r,{name:e!=null?e:P("sh:")}),o=this.subscribe(u=>{if((a!=null?a:Object.is)(n,u))return;n=u;let b=S(u);(i!=null?i:Object.is)(r,b)||(r=b,s.setState(b))},{skipFirst:!0}),c=s.subscribe.bind(s),l={getState:s.getState.bind(s),subscribe:s.subscribe.bind(s),createSelectorHook:v.bind(c),createObservable:A.bind(c),dispose:()=>{o(),s.dispose()},subscribers:s.subscribers};return Object.assign(c,l),c}function v(S,t){let e=t==null?void 0:t.name,a=t==null?void 0:t.isEqualRoot,i=t==null?void 0:t.isEqual,n=this.getState(),r=(S!=null?S:u=>u)(n),s=new g(r,{name:e!=null?e:P("sh:")}),o=this.subscribe(u=>{if((a!=null?a:Object.is)(n,u))return;n=u;let b=S(u);(i!=null?i:Object.is)(r,b)||(r=b,s.setState(b))},{skipFirst:!0}),c=(...u)=>{let[d]=s.use(...u);return d},l={getState:()=>s.getState(),subscribe:s.subscribe.bind(s),createSelectorHook:v.bind(c),createObservable:A.bind(c),dispose:()=>{o(),s.dispose()},subscribers:s.subscribers};return Object.assign(c,l),c}var nt=g;export{g as GlobalStore,A as createObservable,v as createSelectorHook,nt as default};
|
package/README.md
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
# react-hooks-global-states 🌟
|
|
2
2
|
|
|
3
|
-

|
|
4
4
|
|
|
5
5
|
<div align="center">
|
|
6
6
|
|
|
7
|
-
**
|
|
7
|
+
**Shared React state. Precise subscriptions. Built-in DevTools.** 🚀
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
_The familiarity of `useState`, with the power of a shared store._
|
|
10
10
|
|
|
11
11
|
[](https://www.npmjs.com/package/react-hooks-global-states)
|
|
12
12
|
[](https://www.npmjs.com/package/react-hooks-global-states)
|
|
13
13
|
[](https://github.com/johnny-quesada-developer/react-hooks-global-states/blob/main/LICENSE)
|
|
14
14
|
|
|
15
|
-
[**Live Demo**](https://johnny-quesada-developer.github.io/global-hooks-example/) • [**Video Tutorial**](https://www.youtube.com/watch?v=1UBqXk2MH8I
|
|
15
|
+
[**Live Demo**](https://johnny-quesada-developer.github.io/global-hooks-example/) • [**Video Tutorial**](https://www.youtube.com/watch?v=1UBqXk2MH8I) • [**CodePen**](https://codepen.io/johnnynabetes/pen/WNmeGwb?editors=0010)
|
|
16
16
|
|
|
17
17
|
</div>
|
|
18
18
|
|
|
@@ -26,7 +26,7 @@ import { createGlobalState } from 'react-hooks-global-states';
|
|
|
26
26
|
export const useCounter = createGlobalState(0);
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
-
**That's it.** No providers. No context boilerplate. No configuration files.
|
|
29
|
+
**That's it.** No providers. No context boilerplate. No configuration files. Shared state with a familiar React API.
|
|
30
30
|
|
|
31
31
|
```tsx
|
|
32
32
|
// Use it anywhere, instantly
|
|
@@ -38,13 +38,13 @@ function Counter() {
|
|
|
38
38
|
|
|
39
39
|
---
|
|
40
40
|
|
|
41
|
-
## 🚀
|
|
41
|
+
## 🚀 Built for React applications
|
|
42
42
|
|
|
43
43
|
<table>
|
|
44
44
|
<tr>
|
|
45
45
|
<td width="50%">
|
|
46
46
|
|
|
47
|
-
### 🎓 **
|
|
47
|
+
### 🎓 **Familiar React API**
|
|
48
48
|
|
|
49
49
|
```tsx
|
|
50
50
|
// If you know this...
|
|
@@ -57,9 +57,9 @@ const [state, setState] = useGlobalState();
|
|
|
57
57
|
</td>
|
|
58
58
|
<td width="50%">
|
|
59
59
|
|
|
60
|
-
### ⚡ **
|
|
60
|
+
### ⚡ **Precise subscriptions**
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
Subscribe to a slice so unrelated store changes do not trigger component updates.
|
|
63
63
|
|
|
64
64
|
```tsx
|
|
65
65
|
// Only re-renders when name changes
|
|
@@ -75,7 +75,7 @@ const [name] = useStore((s) => s.user.name);
|
|
|
75
75
|
### 🔗 **Chainable Selectors**
|
|
76
76
|
|
|
77
77
|
```tsx
|
|
78
|
-
const useUsers =
|
|
78
|
+
const useUsers = useStore.createSelectorHook((s) => s.users);
|
|
79
79
|
const useAdmins = useUsers.createSelectorHook((users) => users.filter((u) => u.isAdmin));
|
|
80
80
|
```
|
|
81
81
|
|
|
@@ -116,7 +116,7 @@ const Form = createContext({ name: '', email: '' });
|
|
|
116
116
|
</td>
|
|
117
117
|
<td width="50%">
|
|
118
118
|
|
|
119
|
-
###
|
|
119
|
+
### 📦 **Non-Reactive API**
|
|
120
120
|
|
|
121
121
|
Use state anywhere - even outside React components!
|
|
122
122
|
|
|
@@ -167,59 +167,53 @@ function ThemedComponent() {
|
|
|
167
167
|
}
|
|
168
168
|
```
|
|
169
169
|
|
|
170
|
-
###
|
|
170
|
+
### Async state with actions
|
|
171
|
+
|
|
172
|
+
Keep loading and error values in state so components update throughout the request.
|
|
171
173
|
|
|
172
174
|
```tsx
|
|
173
175
|
import { createGlobalState } from 'react-hooks-global-states';
|
|
174
176
|
|
|
177
|
+
type User = { id: string; name: string };
|
|
178
|
+
|
|
175
179
|
const useAuth = createGlobalState(
|
|
176
|
-
{ user: null,
|
|
180
|
+
() => ({ user: null as User | null, isLoading: false, error: null as string | null }),
|
|
177
181
|
{
|
|
178
|
-
// Non-reactive metadata
|
|
179
|
-
metadata: { isLoading: false },
|
|
180
|
-
|
|
181
|
-
// Type-safe actions
|
|
182
182
|
actions: {
|
|
183
|
-
login(
|
|
184
|
-
return async ({ setState
|
|
185
|
-
|
|
183
|
+
login(authenticate: () => Promise<User>) {
|
|
184
|
+
return async ({ setState }) => {
|
|
185
|
+
setState((state) => ({ ...state, isLoading: true, error: null }));
|
|
186
186
|
try {
|
|
187
|
-
const
|
|
188
|
-
setState({ user,
|
|
189
|
-
return { success: true };
|
|
187
|
+
const user = await authenticate();
|
|
188
|
+
setState({ user, isLoading: false, error: null });
|
|
190
189
|
} catch (error) {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
190
|
+
setState((state) => ({
|
|
191
|
+
...state,
|
|
192
|
+
isLoading: false,
|
|
193
|
+
error: error instanceof Error ? error.message : 'Sign-in failed',
|
|
194
|
+
}));
|
|
194
195
|
}
|
|
195
196
|
};
|
|
196
197
|
},
|
|
197
|
-
|
|
198
198
|
logout() {
|
|
199
199
|
return ({ setState }) => {
|
|
200
|
-
setState({ user: null,
|
|
200
|
+
setState({ user: null, isLoading: false, error: null });
|
|
201
201
|
};
|
|
202
202
|
},
|
|
203
203
|
},
|
|
204
204
|
},
|
|
205
205
|
);
|
|
206
206
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
const [auth, actions] = useAuth();
|
|
210
|
-
const { isLoading } = useAuth.getMetadata();
|
|
211
|
-
|
|
212
|
-
const handleSubmit = async (e) => {
|
|
213
|
-
e.preventDefault();
|
|
214
|
-
const result = await actions.login(email, password);
|
|
215
|
-
if (!result.success) toast.error(result.error);
|
|
216
|
-
};
|
|
207
|
+
function LoginButton({ authenticate }: { authenticate: () => Promise<User> }) {
|
|
208
|
+
const [{ isLoading, error }, actions] = useAuth();
|
|
217
209
|
|
|
218
210
|
return (
|
|
219
|
-
<
|
|
220
|
-
{
|
|
221
|
-
|
|
222
|
-
|
|
211
|
+
<div>
|
|
212
|
+
<button disabled={isLoading} onClick={() => void actions.login(authenticate)}>
|
|
213
|
+
{isLoading ? 'Signing in…' : 'Sign in'}
|
|
214
|
+
</button>
|
|
215
|
+
{error && <p role="alert">{error}</p>}
|
|
216
|
+
</div>
|
|
223
217
|
);
|
|
224
218
|
}
|
|
225
219
|
```
|
|
@@ -257,7 +251,7 @@ const useExpensiveState = createGlobalState(() => {
|
|
|
257
251
|
|
|
258
252
|
#### 🎯 Surgical Re-renders with Selectors
|
|
259
253
|
|
|
260
|
-
|
|
254
|
+
Selectors keep components subscribed to the state they use.
|
|
261
255
|
|
|
262
256
|
```tsx
|
|
263
257
|
const useStore = createGlobalState({
|
|
@@ -297,16 +291,16 @@ function NotificationCount() {
|
|
|
297
291
|
}
|
|
298
292
|
```
|
|
299
293
|
|
|
300
|
-
**
|
|
294
|
+
**Subscription behavior:**
|
|
301
295
|
|
|
302
|
-
|
|
|
303
|
-
|
|
|
304
|
-
|
|
|
305
|
-
|
|
|
296
|
+
| Subscription | What triggers an update |
|
|
297
|
+
| --- | --- |
|
|
298
|
+
| `useStore()` | Any store value change |
|
|
299
|
+
| `useStore(selector)` | A change to the selected value |
|
|
306
300
|
|
|
307
301
|
#### ⚡ Computed Values with Dependencies
|
|
308
302
|
|
|
309
|
-
Derive values
|
|
303
|
+
Derive values from both store state and component-local values, with explicit dependencies.
|
|
310
304
|
|
|
311
305
|
```tsx
|
|
312
306
|
const useStore = createGlobalState({
|
|
@@ -382,9 +376,9 @@ function AdvancedFiltered() {
|
|
|
382
376
|
}
|
|
383
377
|
```
|
|
384
378
|
|
|
385
|
-
#### 🔗 Reusable Selector Hooks
|
|
379
|
+
#### 🔗 Reusable Selector Hooks
|
|
386
380
|
|
|
387
|
-
|
|
381
|
+
Compose reusable selector hooks. Each hook returns its selected value directly.
|
|
388
382
|
|
|
389
383
|
```tsx
|
|
390
384
|
const useStore = createGlobalState({
|
|
@@ -412,9 +406,9 @@ const useCurrentUser = useStore.createSelectorHook((state) => {
|
|
|
412
406
|
|
|
413
407
|
// Now use them anywhere!
|
|
414
408
|
function UserStats() {
|
|
415
|
-
const
|
|
416
|
-
const
|
|
417
|
-
const
|
|
409
|
+
const totalUsers = useUsers();
|
|
410
|
+
const activeUsers = useActiveUsers();
|
|
411
|
+
const activeAdmins = useActiveAdmins();
|
|
418
412
|
|
|
419
413
|
return (
|
|
420
414
|
<div>
|
|
@@ -426,19 +420,24 @@ function UserStats() {
|
|
|
426
420
|
}
|
|
427
421
|
|
|
428
422
|
function CurrentUserProfile() {
|
|
429
|
-
const
|
|
423
|
+
const user = useCurrentUser();
|
|
430
424
|
return <div>{user?.name}</div>;
|
|
431
425
|
}
|
|
432
426
|
|
|
433
|
-
//
|
|
434
|
-
function
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
427
|
+
// Selector hooks return values. Update the source store through its API.
|
|
428
|
+
function ActivateAll() {
|
|
429
|
+
return (
|
|
430
|
+
<button
|
|
431
|
+
onClick={() =>
|
|
432
|
+
useStore.setState((state) => ({
|
|
433
|
+
...state,
|
|
434
|
+
users: state.users.map((user) => ({ ...user, active: true })),
|
|
435
|
+
}))
|
|
436
|
+
}
|
|
437
|
+
>
|
|
438
|
+
Activate all users
|
|
439
|
+
</button>
|
|
440
|
+
);
|
|
442
441
|
}
|
|
443
442
|
```
|
|
444
443
|
|
|
@@ -595,7 +594,7 @@ const unsubscribe = useAuth.subscribe(
|
|
|
595
594
|
);
|
|
596
595
|
```
|
|
597
596
|
|
|
598
|
-
#### 🔭 Observable Fragments
|
|
597
|
+
#### 🔭 Observable Fragments
|
|
599
598
|
|
|
600
599
|
Create observable slices of your state for reactive programming!
|
|
601
600
|
|
|
@@ -625,46 +624,23 @@ const doubledObservable = countObservable.createObservable((count) => count * 2)
|
|
|
625
624
|
|
|
626
625
|
#### 📋 Metadata - Non-Reactive Side Info
|
|
627
626
|
|
|
628
|
-
|
|
627
|
+
Keep request counters and timestamps in metadata when they are read only by handlers or background work.
|
|
628
|
+
Put loading indicators and displayed errors in state so components subscribe to their changes.
|
|
629
629
|
|
|
630
630
|
```tsx
|
|
631
631
|
const useStore = createGlobalState(
|
|
632
|
-
{ items: [] },
|
|
633
|
-
{
|
|
634
|
-
metadata: {
|
|
635
|
-
isLoading: false,
|
|
636
|
-
lastFetch: null,
|
|
637
|
-
error: null,
|
|
638
|
-
retryCount: 0,
|
|
639
|
-
},
|
|
640
|
-
},
|
|
632
|
+
{ items: [] as string[] },
|
|
633
|
+
{ metadata: { requests: 0, lastFetchedAt: null as number | null } },
|
|
641
634
|
);
|
|
642
635
|
|
|
643
|
-
// Metadata
|
|
644
|
-
useStore.setMetadata({
|
|
645
|
-
|
|
646
|
-
// But you can access it anytime
|
|
647
|
-
const meta = useStore.getMetadata();
|
|
648
|
-
console.log(meta.isLoading); // true
|
|
636
|
+
// Metadata is replaced, so preserve the fields you are not updating.
|
|
637
|
+
useStore.setMetadata((metadata) => ({ ...metadata, requests: metadata.requests + 1 }));
|
|
649
638
|
|
|
650
|
-
//
|
|
651
|
-
async function fetchData() {
|
|
652
|
-
useStore.setMetadata({ isLoading: true, error: null });
|
|
639
|
+
console.log(useStore.metadata.requests); // 1
|
|
653
640
|
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
useStore.setMetadata({
|
|
658
|
-
isLoading: false,
|
|
659
|
-
lastFetch: new Date(),
|
|
660
|
-
});
|
|
661
|
-
} catch (error) {
|
|
662
|
-
useStore.setMetadata({
|
|
663
|
-
isLoading: false,
|
|
664
|
-
error: error.message,
|
|
665
|
-
retryCount: useStore.getMetadata().retryCount + 1,
|
|
666
|
-
});
|
|
667
|
-
}
|
|
641
|
+
function recordFetchedItems(items: string[]) {
|
|
642
|
+
useStore.setMetadata((metadata) => ({ ...metadata, lastFetchedAt: Date.now() }));
|
|
643
|
+
useStore.setState({ items });
|
|
668
644
|
}
|
|
669
645
|
```
|
|
670
646
|
|
|
@@ -745,7 +721,7 @@ function App() {
|
|
|
745
721
|
|
|
746
722
|
#### 🎯 Context + Selectors = ❤️
|
|
747
723
|
|
|
748
|
-
|
|
724
|
+
Use the same selectors, actions and metadata with state scoped to each provider.
|
|
749
725
|
|
|
750
726
|
```tsx
|
|
751
727
|
const FormContext = createContext({
|
|
@@ -845,7 +821,7 @@ function App() {
|
|
|
845
821
|
}
|
|
846
822
|
|
|
847
823
|
function UserList() {
|
|
848
|
-
const
|
|
824
|
+
const activeUsers = useActiveUsers();
|
|
849
825
|
return (
|
|
850
826
|
<ul>
|
|
851
827
|
{activeUsers.map((u) => (
|
|
@@ -1016,7 +992,7 @@ const SessionContext = createContext(
|
|
|
1016
992
|
},
|
|
1017
993
|
);
|
|
1018
994
|
|
|
1019
|
-
// Internal actions are not
|
|
995
|
+
// Internal actions are not exposed through the context
|
|
1020
996
|
const { loadData } = SessionContext.use.actions();
|
|
1021
997
|
console.log(loadData); // undefined
|
|
1022
998
|
```
|
|
@@ -1342,9 +1318,9 @@ function TodoApp() {
|
|
|
1342
1318
|
}
|
|
1343
1319
|
```
|
|
1344
1320
|
|
|
1345
|
-
**
|
|
1321
|
+
**Benefits of this structure:**
|
|
1346
1322
|
|
|
1347
|
-
- ✅ **
|
|
1323
|
+
- ✅ **Focused modules** - Keep related state, actions and selectors easy to navigate
|
|
1348
1324
|
- ✅ **Type-safe** - Everything is strongly typed
|
|
1349
1325
|
- ✅ **Public/Private APIs** - Internal actions don't pollute public interface
|
|
1350
1326
|
- ✅ **Namespace pattern** - Everything bundled: `todos$.useActiveTodos()`, `todos$.activeTodos$`
|
|
@@ -1548,7 +1524,7 @@ actions.addTodo('Build feature', userId);
|
|
|
1548
1524
|
| Resource | Description |
|
|
1549
1525
|
| ------------------------------------------------------------------------------------ | --------------------------------- |
|
|
1550
1526
|
| 🎮 [**Live Demo**](https://johnny-quesada-developer.github.io/global-hooks-example/) | Interactive examples |
|
|
1551
|
-
| 🎥 [**Video Tutorial**](https://www.youtube.com/watch?v=1UBqXk2MH8I
|
|
1527
|
+
| 🎥 [**Video Tutorial**](https://www.youtube.com/watch?v=1UBqXk2MH8I) | Full walkthrough |
|
|
1552
1528
|
| 💻 [**CodePen**](https://codepen.io/johnnynabetes/pen/WNmeGwb?editors=0010) | Try it online |
|
|
1553
1529
|
| 📚 **400+ Tests** | Check the test suite for patterns |
|
|
1554
1530
|
|
|
@@ -1566,18 +1542,7 @@ actions.addTodo('Build feature', userId);
|
|
|
1566
1542
|
|
|
1567
1543
|
## 🎉 Why Developers Choose This
|
|
1568
1544
|
|
|
1569
|
-
|
|
1570
|
-
"I replaced 500 lines of Redux with 50 lines of this. Mind blown." 🤯
|
|
1571
|
-
- Every developer who tries it
|
|
1572
|
-
|
|
1573
|
-
"The useState I always wanted." ❤️
|
|
1574
|
-
- React developers everywhere
|
|
1575
|
-
|
|
1576
|
-
"Finally, state management that doesn't fight me." 🥊
|
|
1577
|
-
- Tired developers worldwide
|
|
1578
|
-
```
|
|
1579
|
-
|
|
1580
|
-
### The Bottom Line
|
|
1545
|
+
### At a glance
|
|
1581
1546
|
|
|
1582
1547
|
| What You Get | What You Don't |
|
|
1583
1548
|
| --------------------------- | --------------------- |
|
|
@@ -1587,7 +1552,7 @@ actions.addTodo('Build feature', userId);
|
|
|
1587
1552
|
| ✅ TypeScript inference | ❌ Manual typing |
|
|
1588
1553
|
| ✅ Global + Context | ❌ Either/or choice |
|
|
1589
1554
|
| ✅ Actions (optional) | ❌ Required structure |
|
|
1590
|
-
| ✅
|
|
1555
|
+
| ✅ Familiar hook syntax | ❌ New component APIs |
|
|
1591
1556
|
|
|
1592
1557
|
---
|
|
1593
1558
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-hooks-global-states",
|
|
3
|
-
"version": "16.0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "16.0.5-beta.0",
|
|
4
|
+
"description": "The shared state engine for React and React Native: selective subscriptions, derived state, actions and scoped stores.",
|
|
5
5
|
"main": "./bundle.cjs",
|
|
6
6
|
"module": "./bundle.mjs",
|
|
7
7
|
"types": "./index.d.ts",
|
|
@@ -62,6 +62,9 @@
|
|
|
62
62
|
"import": "./debug.mjs",
|
|
63
63
|
"require": "./debug.cjs"
|
|
64
64
|
},
|
|
65
|
+
"./rgsh": {
|
|
66
|
+
"import": "./rgsh.mjs"
|
|
67
|
+
},
|
|
65
68
|
"./package.json": "./package.json"
|
|
66
69
|
},
|
|
67
70
|
"files": [
|
|
@@ -94,7 +97,7 @@
|
|
|
94
97
|
"homepage": "https://github.com/johnny-quesada-developer/react-hooks-global-states#readme",
|
|
95
98
|
"dependencies": {
|
|
96
99
|
"json-storage-formatter": "^4.0.0",
|
|
97
|
-
"react-hooks-global-states-debug": "^16.0.
|
|
100
|
+
"react-hooks-global-states-debug": "^16.0.5-beta.0"
|
|
98
101
|
},
|
|
99
102
|
"peerDependencies": {
|
|
100
103
|
"react": ">=18.0.0",
|
|
@@ -104,5 +107,8 @@
|
|
|
104
107
|
"react": {
|
|
105
108
|
"optional": false
|
|
106
109
|
}
|
|
110
|
+
},
|
|
111
|
+
"bin": {
|
|
112
|
+
"rgsh": "./rgsh.mjs"
|
|
107
113
|
}
|
|
108
114
|
}
|
package/rgsh.mjs
ADDED
package/types.d.ts
CHANGED
|
@@ -58,7 +58,7 @@ export type StateApi<State, StateMutator, Metadata extends BaseMetadata> = {
|
|
|
58
58
|
getMetadata: MetadataGetter<Metadata>;
|
|
59
59
|
/**
|
|
60
60
|
* Sets the metadata value
|
|
61
|
-
* The metadata value is not reactive and
|
|
61
|
+
* The metadata value is not reactive and won’t trigger re-renders
|
|
62
62
|
*/
|
|
63
63
|
setMetadata: MetadataSetter<Metadata>;
|
|
64
64
|
/**
|
|
@@ -83,10 +83,9 @@ export type StateApi<State, StateMutator, Metadata extends BaseMetadata> = {
|
|
|
83
83
|
*/
|
|
84
84
|
subscribe: SubscribeToState<State>;
|
|
85
85
|
/***
|
|
86
|
-
* @description Creates a
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
* you can customize the equality function by passing the isEqualRoot and isEqual parameters
|
|
86
|
+
* @description Creates a read-only hook that returns the selected value directly.
|
|
87
|
+
* Selector hooks can be chained to derive further values.
|
|
88
|
+
* `isEqualRoot` controls recomputation; `isEqual` controls updates to the derived value.
|
|
90
89
|
*/
|
|
91
90
|
createSelectorHook: <Selection>(selector: (state: State) => Selection, args?: Omit<UseHookOptions<Selection, State>, 'dependencies'> & {
|
|
92
91
|
name?: string;
|
|
@@ -110,7 +109,7 @@ export type StateApi<State, StateMutator, Metadata extends BaseMetadata> = {
|
|
|
110
109
|
select: SelectHook<State>;
|
|
111
110
|
/**
|
|
112
111
|
* @description Sugared hook to use the global state in React components
|
|
113
|
-
*
|
|
112
|
+
* Access the hook through the store’s `use` property
|
|
114
113
|
*
|
|
115
114
|
* @example
|
|
116
115
|
* ```tsx
|
|
@@ -189,7 +188,7 @@ export interface StateHook<State, StateMutator, Metadata extends BaseMetadata> e
|
|
|
189
188
|
}
|
|
190
189
|
/**
|
|
191
190
|
* @description Function to set the metadata value
|
|
192
|
-
* The metadata value is not reactive and
|
|
191
|
+
* The metadata value is not reactive and won’t trigger re-renders
|
|
193
192
|
*/
|
|
194
193
|
export type MetadataSetter<Metadata extends BaseMetadata> = (setter: Metadata | ((metadata: Metadata) => Metadata)) => void;
|
|
195
194
|
/**
|
|
@@ -632,35 +631,25 @@ export type ContextPublicApi<State, StateMutator, Metadata extends BaseMetadata>
|
|
|
632
631
|
* - isEqual: A function to compare the current and next selected fragment for equality.
|
|
633
632
|
* - isEqualRoot: A function to compare the entire state for equality.
|
|
634
633
|
* - name: An optional name for debugging purposes.
|
|
635
|
-
* @returns A
|
|
636
|
-
* along with the state mutator and metadata.
|
|
634
|
+
* @returns A read-only context hook that returns the selected value directly.
|
|
637
635
|
*
|
|
638
636
|
* @example
|
|
639
637
|
* ```tsx
|
|
640
|
-
* const
|
|
641
|
-
*
|
|
642
|
-
*
|
|
643
|
-
* }
|
|
644
|
-
*
|
|
645
|
-
*
|
|
646
|
-
* ...
|
|
638
|
+
* const todos = createContext({ filter: '' }, {
|
|
639
|
+
* actions: {
|
|
640
|
+
* setFilter(filter: string) {
|
|
641
|
+
* return ({ setState }) => setState({ filter });
|
|
642
|
+
* },
|
|
643
|
+
* },
|
|
647
644
|
* });
|
|
648
645
|
*
|
|
649
|
-
* const useFilter =
|
|
650
|
-
* return state.filter;
|
|
651
|
-
* });
|
|
646
|
+
* const useFilter = todos.use.createSelectorHook((state) => state.filter);
|
|
652
647
|
*
|
|
653
648
|
* function FilterComponent() {
|
|
654
|
-
*
|
|
655
|
-
*
|
|
656
|
-
* const [filter, { setFilter }] = useFilter();
|
|
649
|
+
* const filter = useFilter();
|
|
650
|
+
* const { setFilter } = todos.use.actions();
|
|
657
651
|
*
|
|
658
|
-
* return (
|
|
659
|
-
* <input
|
|
660
|
-
* value={filter}
|
|
661
|
-
* onChange={(e) => setFilter(e.target.value)}
|
|
662
|
-
* />
|
|
663
|
-
* );
|
|
652
|
+
* return <input value={filter} onChange={(event) => setFilter(event.target.value)} />;
|
|
664
653
|
* }
|
|
665
654
|
* ```
|
|
666
655
|
*/
|
|
@@ -749,10 +738,10 @@ export interface CreateContext {
|
|
|
749
738
|
*
|
|
750
739
|
* There are two ways to use the hook
|
|
751
740
|
* @example
|
|
752
|
-
*
|
|
741
|
+
* Destructure `use` to name the context hook:
|
|
753
742
|
*
|
|
754
743
|
* ```tsx
|
|
755
|
-
* const { Context, Provider,
|
|
744
|
+
* const { Context, Provider, use: useUser} = createContext({ name: 'John', age: 30 });
|
|
756
745
|
*
|
|
757
746
|
* function UserProfile() {
|
|
758
747
|
* const [state, setState, metadata] = useUser();
|
|
@@ -762,7 +751,7 @@ export interface CreateContext {
|
|
|
762
751
|
* ```
|
|
763
752
|
*
|
|
764
753
|
* @example
|
|
765
|
-
*
|
|
754
|
+
* Access the hook and provider through the context object:
|
|
766
755
|
*
|
|
767
756
|
* ```tsx
|
|
768
757
|
* const user = createContext({ name: 'John', age: 30 });
|
|
@@ -795,7 +784,7 @@ export interface CreateContext {
|
|
|
795
784
|
* @param args.name Optional name for debugging purposes.
|
|
796
785
|
* @param args.metadata Optional non-reactive metadata associated with the state.
|
|
797
786
|
* @param args.callbacks Optional lifecycle callbacks for the context.
|
|
798
|
-
* @param args.actions Optional actions
|
|
787
|
+
* @param args.actions Optional actions returned as the hook’s second item; `setState` remains available on the store API.
|
|
799
788
|
* @returns An object containing:
|
|
800
789
|
* - **`use`** — A custom hook to read and mutate the context state.
|
|
801
790
|
* Supports selectors for granular subscriptions and returns `[state, stateMutator, metadata]`.
|
|
@@ -814,7 +803,7 @@ export interface CreateContext {
|
|
|
814
803
|
* @param args.name Optional name for debugging purposes.
|
|
815
804
|
* @param args.metadata Optional non-reactive metadata associated with the state.
|
|
816
805
|
* @param args.callbacks Optional lifecycle callbacks for the context.
|
|
817
|
-
* @param args.actions Optional actions
|
|
806
|
+
* @param args.actions Optional actions returned as the hook’s second item; `setState` remains available on the store API.
|
|
818
807
|
*/
|
|
819
808
|
args: {
|
|
820
809
|
name?: string;
|
|
@@ -828,10 +817,10 @@ export interface CreateContext {
|
|
|
828
817
|
*
|
|
829
818
|
* There are two ways to use the hook
|
|
830
819
|
* @example
|
|
831
|
-
*
|
|
820
|
+
* Destructure `use` to name the context hook:
|
|
832
821
|
*
|
|
833
822
|
* ```tsx
|
|
834
|
-
* const { Context, Provider,
|
|
823
|
+
* const { Context, Provider, use: useUser} = createContext({ name: 'John', age: 30 });
|
|
835
824
|
*
|
|
836
825
|
* function UserProfile() {
|
|
837
826
|
* const [state, setState, metadata] = useUser();
|
|
@@ -841,7 +830,7 @@ export interface CreateContext {
|
|
|
841
830
|
* ```
|
|
842
831
|
*
|
|
843
832
|
* @example
|
|
844
|
-
*
|
|
833
|
+
* Access the hook and provider through the context object:
|
|
845
834
|
*
|
|
846
835
|
* ```tsx
|
|
847
836
|
* const user = createContext({ name: 'John', age: 30 });
|
|
@@ -874,7 +863,7 @@ export interface CreateContext {
|
|
|
874
863
|
* @param args.name Optional name for debugging purposes.
|
|
875
864
|
* @param args.metadata Optional non-reactive metadata associated with the state.
|
|
876
865
|
* @param args.callbacks Optional lifecycle callbacks for the context.
|
|
877
|
-
* @param args.actions Optional actions
|
|
866
|
+
* @param args.actions Optional actions returned as the hook’s second item; `setState` remains available on the store API.
|
|
878
867
|
* @returns An object containing:
|
|
879
868
|
* - **`use`** — A custom hook to read and mutate the context state.
|
|
880
869
|
* Supports selectors for granular subscriptions and returns `[state, stateMutator, metadata]`.
|
|
@@ -893,7 +882,7 @@ export interface CreateContext {
|
|
|
893
882
|
* @param args.name Optional name for debugging purposes.
|
|
894
883
|
* @param args.metadata Optional non-reactive metadata associated with the state.
|
|
895
884
|
* @param args.callbacks Optional lifecycle callbacks for the context.
|
|
896
|
-
* @param args.actions Optional actions
|
|
885
|
+
* @param args.actions Optional actions returned as the hook’s second item; `setState` remains available on the store API.
|
|
897
886
|
*/
|
|
898
887
|
args: {
|
|
899
888
|
name?: string;
|
|
@@ -907,10 +896,10 @@ export interface CreateContext {
|
|
|
907
896
|
*
|
|
908
897
|
* There are two ways to use the hook
|
|
909
898
|
* @example
|
|
910
|
-
*
|
|
899
|
+
* Destructure `use` to name the context hook:
|
|
911
900
|
*
|
|
912
901
|
* ```tsx
|
|
913
|
-
* const { Context, Provider,
|
|
902
|
+
* const { Context, Provider, use: useUser} = createContext({ name: 'John', age: 30 });
|
|
914
903
|
*
|
|
915
904
|
* function UserProfile() {
|
|
916
905
|
* const [state, setState, metadata] = useUser();
|
|
@@ -920,7 +909,7 @@ export interface CreateContext {
|
|
|
920
909
|
* ```
|
|
921
910
|
*
|
|
922
911
|
* @example
|
|
923
|
-
*
|
|
912
|
+
* Access the hook and provider through the context object:
|
|
924
913
|
*
|
|
925
914
|
* ```tsx
|
|
926
915
|
* const user = createContext({ name: 'John', age: 30 });
|
|
@@ -1038,7 +1027,7 @@ export interface CreateGlobalState {
|
|
|
1038
1027
|
* @param args.name optional name for debugging purposes
|
|
1039
1028
|
* @param args.metadata optional non-reactive metadata associated with the state (can be a value or callback)
|
|
1040
1029
|
* @param args.callbacks optional lifecycle callbacks for the global state
|
|
1041
|
-
* @param args.actions
|
|
1030
|
+
* @param args.actions Optional actions returned as the hook’s second item; `setState` remains available on the store API
|
|
1042
1031
|
* @returns a state hook that you can use in your components
|
|
1043
1032
|
*
|
|
1044
1033
|
* @example
|
|
@@ -1103,7 +1092,7 @@ export interface CreateGlobalState {
|
|
|
1103
1092
|
* @param args.name optional name for debugging purposes
|
|
1104
1093
|
* @param args.metadata optional non-reactive metadata associated with the state (can be a value or callback)
|
|
1105
1094
|
* @param args.callbacks optional lifecycle callbacks for the global state
|
|
1106
|
-
* @param args.actions
|
|
1095
|
+
* @param args.actions Optional actions returned as the hook’s second item; `setState` remains available on the store API
|
|
1107
1096
|
* @returns a state hook that you can use in your components
|
|
1108
1097
|
*
|
|
1109
1098
|
* @example
|