allstore
Version:
A React Single-Truth store in 94% less code-lines by Harald Rudell
56 lines (44 loc) • 4.89 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', { value: true });
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
var React = require('react');
var React__default = _interopDefault(React);
function _defineProperty(obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
return obj;
}
var defineProperty = _defineProperty;
/*
© 2018-present Harald Rudell <harald.rudell@gmail.com> (http://www.haraldrudell.com)
This source code is licensed under the ISC-style license found in the LICENSE file in the root directory of this source tree.
*/const store={};const getState=()=>store;const notify=()=>_subscriptions.forEach(v=>getSubscribeFn(v,true));const subscribe=v=>_subscriptions.push(getSubscribeFn(v))&&{unsubscribe:()=>unsubscribe(v)};const _subscriptions=[];function getSubscribeFn(v,invoke){if(typeof Object(v).next==='function')return invoke?v.next(store):v;if(typeof v==='function')return invoke?v(store):v;throw new Error('plainStore: bad argument to subscribe');}function unsubscribe(v){const i=_subscriptions.indexOf(v);if(i>=0)_subscriptions.splice(i,1);}
const storeContext=React.createContext();const {Provider:StoreProvider,Consumer:StoreConsumer}=storeContext;const contextValue={store,getState,subscribe,notify};function applyStoreProp(storeProp){if(!storeProp)return;Object.getOwnPropertyNames(store).forEach(p=>delete store[p]);Object.assign(store,{...storeProp});}var Store = React.memo(({children,store:storeProp})=>React.useMemo(()=>applyStoreProp(storeProp),[storeProp])||React__default.createElement(StoreProvider,{value:contextValue},children));
const connect=(mapStateToProps,options)=>ConnectedComponent=>connect2(mapStateToProps,ConnectedComponent,options);function connect2(mapStateToProps=store=>store,ConnectedComponent,options){if(!options)options={};const displayName=String(options.displayName||ConnectedComponent.displayName||ConnectedComponent.name||'Connect');const pure=options.pure!==false;if(typeof mapStateToProps!=='function')throw new Error('connect: mapStateToProps not function');class Connect extends(pure?React.PureComponent:React.Component){constructor(...args){super(...args);defineProperty(this,"getSelectors",state=>Object(mapStateToProps(state,this.props)));defineProperty(this,"getStoreProps",()=>this.getSelectors(this.context.getState()));defineProperty(this,"componentDidMount",()=>(this.subscription=this.context.subscribe(this))&&(this.lastProps=this.getStoreProps()));defineProperty(this,"componentWillUnmount",()=>this.subscription.unsubscribe());defineProperty(this,"render",()=>React__default.createElement(ConnectedComponent,Object.assign({},this.props,this.getStoreProps())));}next(state){if(pure){const{lastProps}=this;const newProps=this.getSelectors(state);const list=Object.entries(newProps);if(Object.keys(lastProps).length===list.length&&list.every(([key,value])=>value===lastProps[key]))return;// no change
this.lastProps=newProps;}this.setState({a:Object(this.state).a+1||1});}}Connect.contextType=storeContext;displayName&&(Connect.displayName=displayName);return Connect;}
function useAllstore(selectorFn=store=>store,props,pure){if(typeof selectorFn!=='function')throw new Error('useAllstore: selector not function');props={...props};pure=pure!==false;// default is pure component
// get context containing store
const contextValue=React.useContext(storeContext);if(!contextValue)throw new Error('useAllstore: store context missing, did Store component render?');const{subscribe,store}=contextValue;// generate memoized props, count and propsFromStore
const closure=React.useRef({});const[lastCount,setCount]=React.useState(0);// force redraw mechanic
Object.assign(closure.current,{propsArg:props,lastCount});const storeProps=React.useMemo(()=>closure.current.lastProps=Object(selectorFn(store,props)),pure?[lastCount,...Object.values(props)]:null);// subscribe to store and redraw on updates
const subscription=React.useMemo(()=>subscribe(next),[]);function next(state){const{propsArg,lastCount,lastProps}=closure.current;if(pure){const newEntries=Object.entries(Object(selectorFn(state,propsArg)));if(Object.keys(lastProps).length===newEntries.length&&newEntries.every(([key,value])=>value===lastProps[key]))return;// no change
}setCount(lastCount+1);}React.useEffect(()=>()=>subscription.unsubscribe(),[]);return storeProps;}
exports.store = store;
exports.getState = getState;
exports.subscribe = subscribe;
exports.notify = notify;
exports.storeContext = storeContext;
exports.StoreProvider = StoreProvider;
exports.StoreConsumer = StoreConsumer;
exports.Store = Store;
exports.connect = connect;
exports.connect2 = connect2;
exports.useAllstore = useAllstore;