@sampettersson/constate
Version:
Yet another React state management library that lets you work with local state and scale up to global state with ease
483 lines (383 loc) • 14.1 kB
JavaScript
import { Component, createElement, createContext } from 'react';
var Context = createContext({
state: {}
});
var Consumer = Context.Consumer;
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;
}
function _extends() {
_extends = Object.assign || function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
return _extends.apply(this, arguments);
}
function _objectSpread(target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i] != null ? arguments[i] : {};
var ownKeys = Object.keys(source);
if (typeof Object.getOwnPropertySymbols === 'function') {
ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) {
return Object.getOwnPropertyDescriptor(source, sym).enumerable;
}));
}
ownKeys.forEach(function (key) {
_defineProperty(target, key, source[key]);
});
}
return target;
}
function _inheritsLoose(subClass, superClass) {
subClass.prototype = Object.create(superClass.prototype);
subClass.prototype.constructor = subClass;
subClass.__proto__ = superClass;
}
function _objectWithoutPropertiesLoose(source, excluded) {
if (source == null) return {};
var target = {};
var sourceKeys = Object.keys(source);
var key, i;
for (i = 0; i < sourceKeys.length; i++) {
key = sourceKeys[i];
if (excluded.indexOf(key) >= 0) continue;
target[key] = source[key];
}
return target;
}
function _assertThisInitialized(self) {
if (self === void 0) {
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
}
return self;
}
var mapWith = function mapWith(map, transform) {
return Object.keys(map).reduce(function (final, key) {
var _objectSpread2;
return _objectSpread({}, final, (_objectSpread2 = {}, _objectSpread2[key] = transform(map[key], key), _objectSpread2));
}, {});
};
var mapSetStateToActions = function mapSetStateToActions(setState, actionMap) {
return mapWith(actionMap, function (action, type) {
return function () {
return setState(action.apply(void 0, arguments), undefined, type);
};
});
};
var mapStateToSelectors = function mapStateToSelectors(state, selectorMap) {
return mapWith(selectorMap, function (selector) {
return function () {
return selector.apply(void 0, arguments)(state);
};
});
};
var mapPropsToEffects = function mapPropsToEffects(getEffectProps, effectMap) {
return mapWith(effectMap, function (effect, type) {
return function () {
return effect.apply(void 0, arguments)(getEffectProps(type));
};
});
}; // _ is a temporary fix for eslint parser
var parseUpdater = function parseUpdater(updaterOrState, state) {
return typeof updaterOrState === "function" ? updaterOrState(state) : updaterOrState;
};
var InnerContainer =
/*#__PURE__*/
function (_React$Component) {
_inheritsLoose(InnerContainer, _React$Component);
function InnerContainer(props) {
var _this;
_this = _React$Component.call(this, props) || this;
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "state", _this.props.initialState);
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "ignoreState", false);
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "unmount", undefined);
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "getEffectProps", function (type) {
var _this$props = _this.props,
context = _this$props.context,
state = _this$props.state;
return {
state: context && state ? state : _this.state,
setState: function setState(u, c) {
return _this.handleSetState(u, c, type);
}
};
});
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "handleSetState", function (updater, callback, type) {
var _this$props2 = _this.props,
context = _this$props2.context,
setContextState = _this$props2.setContextState;
var prevState;
var updaterFn = function updaterFn(state) {
prevState = state;
return parseUpdater(updater, state);
};
var callbackFn = function callbackFn() {
var _this$props3 = _this.props,
_this$props3$state = _this$props3.state,
state = _this$props3$state === void 0 ? _this.state : _this$props3$state,
onUpdate = _this$props3.onUpdate;
if (onUpdate && _this.ignoreState !== state) {
onUpdate(_objectSpread({}, _this.getEffectProps("onUpdate"), {
prevState: prevState,
type: type
}));
}
if (callback) callback();
};
if (context && setContextState) {
setContextState(context, updaterFn, callbackFn, type);
} else {
// @ts-ignore
_this.setState(updaterFn, callbackFn);
}
});
var _context = props.context,
_setContextState = props.setContextState,
_state = props.state,
initialState = props.initialState;
if (_context && _setContextState && !_state) {
_setContextState(_context, function (currentState) {
return Object.assign({}, initialState, currentState);
}, undefined, "initialState");
}
return _this;
}
var _proto = InnerContainer.prototype;
_proto.componentDidMount = function componentDidMount() {
var _this2 = this;
var _this$props4 = this.props,
context = _this$props4.context,
mountContainer = _this$props4.mountContainer,
onMount = _this$props4.onMount;
var mount = function mount() {
return onMount && onMount(_this2.getEffectProps("onMount"));
};
if (context && mountContainer) {
this.unmount = mountContainer(context, mount);
} else if (!context) {
mount();
}
};
_proto.shouldComponentUpdate = function shouldComponentUpdate(nextProps, nextState) {
var _this3 = this;
var _this$props5 = this.props,
context = _this$props5.context,
stateFromProps = _this$props5.state;
var nextStateFromProps = nextProps.state,
shouldUpdate = nextProps.shouldUpdate;
var couldUpdate = true;
if (context && stateFromProps && nextStateFromProps && shouldUpdate) {
couldUpdate = shouldUpdate({
state: stateFromProps,
setState: function setState(u, c) {
return _this3.handleSetState(u, c, "shouldUpdate");
},
nextState: nextStateFromProps
});
this.ignoreState = !couldUpdate && nextStateFromProps;
} else if (!context && shouldUpdate) {
couldUpdate = shouldUpdate({
state: this.state,
setState: function setState(u, c) {
return _this3.handleSetState(u, c, "shouldUpdate");
},
nextState: nextState
});
this.ignoreState = !couldUpdate && nextState;
}
return couldUpdate;
};
_proto.componentWillUnmount = function componentWillUnmount() {
var _this4 = this;
var _this$props6 = this.props,
context = _this$props6.context,
onUnmount = _this$props6.onUnmount;
var unmount = function unmount() {
return onUnmount && onUnmount(_this4.getEffectProps("onUnmount"));
};
if (this.unmount) {
this.unmount(unmount);
} else if (!context) {
unmount();
}
};
_proto.render = function render() {
var _this$props7 = this.props,
state = _this$props7.state,
children = _this$props7.children,
actions = _this$props7.actions,
selectors = _this$props7.selectors,
effects = _this$props7.effects;
var childrenProps = Object.assign({}, state || this.state, actions && mapSetStateToActions(this.handleSetState, actions), selectors && mapStateToSelectors(state || this.state, selectors), effects && mapPropsToEffects(this.getEffectProps, effects));
return children(childrenProps);
};
return InnerContainer;
}(Component); // eslint-disable-next-line react/prefer-stateless-function, react/no-multi-comp
_defineProperty(InnerContainer, "defaultProps", {
initialState: {}
});
var Container =
/*#__PURE__*/
function (_React$Component2) {
_inheritsLoose(Container, _React$Component2);
function Container() {
return _React$Component2.apply(this, arguments) || this;
}
var _proto2 = Container.prototype;
_proto2.render = function render() {
var _this5 = this;
var context = this.props.context;
if (typeof context !== "undefined") {
return createElement(Consumer, null, function (_ref) {
var state = _ref.state,
setContextState = _ref.setContextState,
mountContainer = _ref.mountContainer;
return createElement(InnerContainer, _extends({}, _this5.props, {
state: state[context],
setContextState: setContextState,
mountContainer: mountContainer
}));
});
}
return createElement(InnerContainer, this.props);
};
return Container;
}(Component);
var reduxDevtoolsExtension = typeof window !== "undefined" && window.__REDUX_DEVTOOLS_EXTENSION__;
var Provider =
/*#__PURE__*/
function (_React$Component) {
_inheritsLoose(Provider, _React$Component);
function Provider(props) {
var _this;
_this = _React$Component.call(this, props) || this;
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "containers", {});
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "devtools", void 0);
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "mountContainer", function (context, onMount) {
if (!_this.containers[context]) {
_this.containers[context] = 0;
if (onMount) _this.setState(null, onMount);
}
_this.containers[context] += 1;
return function (onUnmount) {
if (_this.containers[context] === 1 && onUnmount) onUnmount();
_this.containers[context] -= 1;
};
});
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "setContextState", function (context, updater, callback, type) {
var prevState;
var updaterFn = function updaterFn(state) {
var _Object$assign;
prevState = state.state;
return {
state: Object.assign({}, state.state, (_Object$assign = {}, _Object$assign[context] = Object.assign({}, state.state[context], parseUpdater(updater, state.state[context] || {})), _Object$assign))
};
};
var callbackFn = function callbackFn() {
if (_this.props.onUpdate) {
var onUpdateProps = _objectSpread({}, _this.getProps("Provider/onUpdate"), {
prevState: prevState,
context: context,
type: type
});
_this.props.onUpdate(onUpdateProps);
}
if (callback) callback(); // istanbul ignore next
if (_this.devtools && type) {
var devtoolsType = context ? context + "/" + type : type;
_this.devtools.send(devtoolsType, _this.state.state);
}
}; // @ts-ignore
_this.setState(updaterFn, callbackFn);
});
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "getProps", function (type) {
var _this$state = _this.state,
state = _this$state.state,
setContextState = _this$state.setContextState;
return {
state: state,
setContextState: function (_setContextState) {
function setContextState(_x, _x2, _x3) {
return _setContextState.apply(this, arguments);
}
setContextState.toString = function () {
return _setContextState.toString();
};
return setContextState;
}(function (context, updater, callback) {
return setContextState(context, updater, callback, type);
})
};
});
var devtools = props.devtools,
initialState = props.initialState;
_this.state = {
state: initialState,
mountContainer: _this.mountContainer,
setContextState: _this.setContextState
}; // istanbul ignore next
if (devtools && reduxDevtoolsExtension) {
_this.devtools = reduxDevtoolsExtension.connect({
name: "Context"
});
_this.devtools.init(initialState);
_this.devtools.subscribe(function (message) {
if (message.type === "DISPATCH" && message.state) {
_this.setState(function (state) {
return {
state: Object.assign({}, state.state, JSON.parse(message.state))
};
});
}
});
}
return _this;
}
var _proto = Provider.prototype;
_proto.componentDidMount = function componentDidMount() {
if (this.props.onMount) {
this.props.onMount(this.getProps("Provider/onMount"));
}
};
_proto.componentWillUnmount = function componentWillUnmount() {
if (this.props.onUnmount) {
var _this$getProps = this.getProps(),
_setContextState2 = _this$getProps.setContextState,
args = _objectWithoutPropertiesLoose(_this$getProps, ["setContextState"]);
this.props.onUnmount(args);
} // istanbul ignore next
if (this.devtools && reduxDevtoolsExtension) {
this.devtools.unsubscribe();
reduxDevtoolsExtension.disconnect();
}
};
_proto.render = function render() {
return (// @ts-ignore
createElement(Context.Provider, {
value: this.state
}, this.props.children)
);
};
return Provider;
}(Component);
_defineProperty(Provider, "defaultProps", {
initialState: {}
});
export { Consumer, Container, Provider };