UNPKG

react-redux-restate

Version:

Reform, rewire, restate redux state, or even states

121 lines (98 loc) 3.54 kB
import React, {Component} from 'react'; import PropTypes from 'prop-types'; import shallowequal from 'shallowequal'; import {createProvider} from 'react-redux'; import reduxRestate from 'redux-restate'; import hoistStatics from 'hoist-react-statics'; import displayName from 'react-display-name'; const getStores = (stores, props, context) => { const result = {}; Object.keys(stores).forEach(key => { const value = stores[key]; if (typeof value === 'string') { const store = props[value] || context[value]; if (store) { result[key] = store; } else { throw new Error(`restate: unable to found store "${value}" for key "${key}"`); } } else { result[key] = value; } }); return result; }; const nullFn = () => ({}); const noChildren = {children: undefined}; const restate = (baseStores, composeState, routeDispatch, options = nullFn) => WrappedComponent => { if(options && typeof options !== 'function') { throw new Error('react-redux-restate: options should be an option') } const basicOptions = options({}); const storeKey = basicOptions.storeKey || 'store'; const restateKey = basicOptions.restateKey || 'store'; const Provider = createProvider(restateKey); const contextTypes = { [storeKey]: PropTypes.any, }; Object.keys(baseStores).forEach(key => { const value = baseStores[key]; if (typeof value === 'string') { contextTypes[value] = PropTypes.any; } }); const ignoredProps = {}; const deeperProps = basicOptions.deeperProps || []; (basicOptions.ignoreProps || []).forEach(prop => {ignoredProps[prop] = undefined}); const compareProps = (nextProps, currentProps, additionalIgnore = {}) => ( shallowequal({...nextProps, ...ignoredProps, ...additionalIgnore}, {...currentProps, ...ignoredProps, ...additionalIgnore}) && deeperProps.reduce((acc, line) => acc && shallowequal(nextProps[line], currentProps[line]), true) ); class RestateComponent extends Component { static contextTypes = contextTypes; constructor(props, context) { super(); this.stores = { ...getStores(baseStores, props, context), default: props[storeKey] || context[storeKey], }; this.propsOverride = props; this.store = reduxRestate(this.stores, this.composeState, this.routeDispatch, this.getOptions()); this.propsOverride = null; } componentDidMount(){ this.store.initialize(); } shouldComponentUpdate(nextProps) { return !compareProps(nextProps, this.props); } componentDidUpdate(prevPops) { if (!compareProps(prevPops, this.props, noChildren)) { this.store.replaceOptions(options(this.props)); this.store.update(); this.propsOverride = null; } } componentWillUnmount() { this.store.unsubscribe(); } getOptions() { return Object.assign(options(this.props), { noAutoSubscribe: true }); } composeState = stores => composeState(stores, this.propsOverride || this.props); routeDispatch = (dispatches, event) => routeDispatch(dispatches, event, this.propsOverride || this.props); render() { return ( <Provider store={this.store}> <WrappedComponent {...this.props} /> </Provider> ); } } hoistStatics(RestateComponent, WrappedComponent); RestateComponent.displayName = `reconnected(${displayName(WrappedComponent)})`; return RestateComponent; }; export default restate;