Socket
Socket
Sign inDemoInstall

constate

Package Overview
Dependencies
6
Maintainers
1
Versions
40
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 0.9.0 to 1.0.0-alpha.0

dist/ts/src/createContext.d.ts

18

CHANGELOG.md

@@ -0,1 +1,15 @@

# Change Log
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
<a name="1.0.0-alpha.0"></a>
# [1.0.0-alpha.0](https://github.com/diegohaz/constate/compare/v0.9.0...v1.0.0-alpha.0) (2018-11-21)
### Features
* Hooks ([#51](https://github.com/diegohaz/constate/issues/51)) ([10dd41d](https://github.com/diegohaz/constate/commit/10dd41d)), closes [#48](https://github.com/diegohaz/constate/issues/48)
<a name="0.9.0"></a>

@@ -68,5 +82,1 @@ # [0.9.0](https://github.com/diegohaz/constate/compare/v0.8.2...v0.9.0) (2018-10-03)

* Add shouldUpdate ([#32](https://github.com/diegohaz/constate/issues/32)) ([88d49f8](https://github.com/diegohaz/constate/commit/88d49f8))

@@ -7,482 +7,216 @@ 'use strict';

var Context = React.createContext({
state: {}
});
var hashMap = {};
function hash(string) {
if (hashMap[string] !== undefined) {
return hashMap[string];
}
var Consumer = Context.Consumer;
var _Object$keys = Object.keys(hashMap),
length = _Object$keys.length;
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;
hashMap[string] = 1 << length;
return hashMap[string];
}
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;
function parseUpdater(state, prevState) {
var isUpdater = function isUpdater(a) {
return typeof a === "function";
};
return _extends.apply(this, arguments);
return isUpdater(state) ? state(prevState) : state;
}
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]);
});
function createUseContextEffect(type) {
if (type === void 0) {
type = "useEffect";
}
return target;
}
var consumers = {};
return function (contextKey, create, inputs) {
var key = contextKey;
var consumer = React.useRef(null);
function _inheritsLoose(subClass, superClass) {
subClass.prototype = Object.create(superClass.prototype);
subClass.prototype.constructor = subClass;
subClass.__proto__ = superClass;
}
if (consumers[key] == null) {
consumers[key] = consumer;
}
function _objectWithoutPropertiesLoose(source, excluded) {
if (source == null) return {};
var target = {};
var sourceKeys = Object.keys(source);
var key, i;
React.useMutationEffect(function () {
if (!key) return undefined;
return function () {
consumers[key] = null;
};
}, [key]);
React[type](function () {
if (!key || consumers[key] === consumer) {
return create();
}
for (i = 0; i < sourceKeys.length; i++) {
key = sourceKeys[i];
if (excluded.indexOf(key) >= 0) continue;
target[key] = source[key];
}
return target;
return undefined;
}, inputs ? [key].concat(inputs) : undefined);
};
}
function _assertThisInitialized(self) {
if (self === void 0) {
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
}
var EmptyContext = React.createContext([]);
return self;
}
function createUseContextReducer(context) {
return function (contextKey, reducer, initialState, initialAction) {
var observedBits = contextKey ? hash(contextKey) : undefined; // @ts-ignore
var keys = Object.keys;
function mapSetStateToActions(setState, actionMap) {
return keys(actionMap).reduce(function (map, type) {
var _Object$assign;
var _React$useContext = React.useContext(contextKey ? context : EmptyContext, observedBits),
contextState = _React$useContext[0],
setContextState = _React$useContext[1];
return Object.assign(map, (_Object$assign = {}, _Object$assign[type] = function () {
return setState(actionMap[type].apply(actionMap, arguments), undefined, type);
}, _Object$assign));
}, {});
}
function mapStateToSelectors(state, selectorMap) {
return keys(selectorMap).reduce(function (map, type) {
var _Object$assign2;
var _React$useReducer = React.useReducer(reducer, initialState, contextKey ? undefined : initialAction),
state = _React$useReducer[0],
dispatch = _React$useReducer[1];
return Object.assign(map, (_Object$assign2 = {}, _Object$assign2[type] = function () {
return selectorMap[type].apply(selectorMap, arguments)(state);
}, _Object$assign2));
}, {});
}
function mapPropsToEffects(getEffectProps, effectMap) {
return keys(effectMap).reduce(function (final, type) {
var _Object$assign3;
if (contextKey) {
if (contextState[contextKey] != null) {
state = contextState[contextKey];
}
return Object.assign(final, (_Object$assign3 = {}, _Object$assign3[type] = function () {
return effectMap[type].apply(effectMap, arguments)(getEffectProps(type));
}, _Object$assign3));
}, {});
}
function parseUpdater(updaterOrState, state) {
return typeof updaterOrState === "function" ? updaterOrState(state) : updaterOrState;
}
dispatch = function dispatch(action) {
return setContextState(function (prevState) {
var _Object$assign;
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);
}
return Object.assign({}, prevState, (_Object$assign = {}, _Object$assign[contextKey] = reducer(prevState[contextKey], action), _Object$assign));
});
};
});
_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;
}
React.useMutationEffect(function () {
if (contextKey && contextState[contextKey] == null && (initialState != null || initialAction)) {
setContextState(function (prevState) {
if (prevState[contextKey] == null) {
var _Object$assign2;
var _proto = InnerContainer.prototype;
return Object.assign({}, prevState, (_Object$assign2 = {}, _Object$assign2[contextKey] = initialAction ? reducer(state, initialAction) : state, _Object$assign2));
}
_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 _this$props5 = this.props,
pure = _this$props5.pure,
context = _this$props5.context,
stateFromProps = _this$props5.state;
var nextStateFromProps = nextProps.state,
shouldUpdate = nextProps.shouldUpdate;
var couldUpdate = true;
if (context && stateFromProps && nextStateFromProps) {
couldUpdate = !pure || stateFromProps !== nextStateFromProps;
if (couldUpdate && shouldUpdate) {
couldUpdate = shouldUpdate({
state: stateFromProps,
nextState: nextStateFromProps
return prevState;
});
this.ignoreState = !couldUpdate && nextStateFromProps;
}
} else if (!context) {
couldUpdate = !pure || this.state !== nextState;
if (couldUpdate && shouldUpdate) {
couldUpdate = shouldUpdate({
state: this.state,
nextState: nextState
});
this.ignoreState = !couldUpdate && nextState;
}
}
return couldUpdate;
}, [contextKey]);
return [state, dispatch];
};
}
_proto.componentWillUnmount = function componentWillUnmount() {
var _this3 = this;
function basicStateReducer(state, action) {
return parseUpdater(action, state);
}
var _this$props6 = this.props,
context = _this$props6.context,
onUnmount = _this$props6.onUnmount;
var unmount = function unmount() {
return onUnmount && onUnmount(_this3.getEffectProps("onUnmount"));
};
if (this.unmount) {
this.unmount(unmount);
} else if (!context) {
unmount();
}
function createUseContextState(context) {
var useContextReducer = createUseContextReducer(context);
return function (contextKey, initialState) {
return useContextReducer(contextKey, basicStateReducer, initialState);
};
}
_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);
};
var devtoolsExtension = typeof window !== "undefined" && window.__REDUX_DEVTOOLS_EXTENSION__;
return InnerContainer;
}(React.Component); // eslint-disable-next-line react/prefer-stateless-function, react/no-multi-comp
function useDevtools(state, setState, _temp) {
var _ref = _temp === void 0 ? {} : _temp,
name = _ref.name,
_ref$enabled = _ref.enabled,
enabled = _ref$enabled === void 0 ? true : _ref$enabled;
_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 _this4 = this;
var context = this.props.context;
if (typeof context !== "undefined") {
return React.createElement(Consumer, null, function (_ref) {
var state = _ref.state,
setContextState = _ref.setContextState,
mountContainer = _ref.mountContainer;
return React.createElement(InnerContainer, _extends({}, _this4.props, {
state: state[context],
setContextState: setContextState,
mountContainer: mountContainer
}));
var devtools = React.useRef(null);
var prevState = React.useRef(null);
React.useEffect(function () {
if (enabled && devtoolsExtension) {
devtools.current = devtoolsExtension.connect({
name: name
});
}
return React.createElement(InnerContainer, this.props);
};
return Container;
}(React.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);
devtools.current.init(state);
devtools.current.subscribe(function (message) {
if (message.type === "DISPATCH" && message.state) {
setState(JSON.parse(message.state));
}
if (callback) callback(); // istanbul ignore next
if (_this.devtools && type) {
var devtoolsType = context ? context + "/" + type : type;
_this.devtools.send(devtoolsType, _this.state.state);
});
return function () {
if (devtools.current) {
devtools.current.unsubscribe();
devtoolsExtension.disconnect();
}
}; // @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
return undefined;
}, [enabled]);
React.useEffect(function () {
if (enabled && devtoolsExtension) {
var changedKey;
if (devtools && reduxDevtoolsExtension) {
_this.devtools = reduxDevtoolsExtension.connect({
name: "Context"
});
for (var key in state) {
if (prevState.current && state[key] !== prevState.current[key]) {
changedKey = key;
}
}
_this.devtools.init(initialState);
if (changedKey && devtools.current) {
devtools.current.send(changedKey, state);
}
_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))
};
});
}
});
prevState.current = state;
}
}, [state, enabled]);
}
return _this;
}
function createContext(initialState, name) {
var Context = React.createContext([initialState, function () {}], function (_ref, _ref2) {
var prev = _ref[0];
var next = _ref2[0];
var changedBits = 0;
var _proto = Provider.prototype;
_proto.componentDidMount = function componentDidMount() {
if (this.props.onMount) {
this.props.onMount(this.getProps("Provider/onMount"));
for (var contextKey in next) {
if (prev[contextKey] !== next[contextKey]) {
changedBits |= hash(contextKey);
}
}
};
_proto.componentWillUnmount = function componentWillUnmount() {
if (this.props.onUnmount) {
var _this$getProps = this.getProps(),
_setContextState2 = _this$getProps.setContextState,
args = _objectWithoutPropertiesLoose(_this$getProps, ["setContextState"]);
return changedBits;
});
this.props.onUnmount(args);
} // istanbul ignore next
if (this.devtools && reduxDevtoolsExtension) {
this.devtools.unsubscribe();
reduxDevtoolsExtension.disconnect();
}
var Provider = function Provider(_ref3) {
var children = _ref3.children,
devtools = _ref3.devtools;
var state = React.useState(initialState);
var value = React.useMemo(function () {
return state;
}, [state[0]]);
useDevtools(state[0], state[1], {
name: name,
enabled: devtools
});
return React.createElement(Context.Provider, {
value: value
}, children);
};
_proto.render = function render() {
return (// @ts-ignore
React.createElement(Context.Provider, {
value: this.state
}, this.props.children)
);
return {
Context: Context,
Provider: Provider,
useContextState: createUseContextState(Context),
useContextReducer: createUseContextReducer(Context),
useContextEffect: createUseContextEffect(),
useContextLayoutEffect: createUseContextEffect("useLayoutEffect"),
useContextMutationEffect: createUseContextEffect("useMutationEffect")
};
}
return Provider;
}(React.Component);
var _createContext = createContext({}),
Context = _createContext.Context,
Provider = _createContext.Provider,
useContextReducer = _createContext.useContextReducer,
useContextState = _createContext.useContextState,
useContextEffect = _createContext.useContextEffect,
useContextLayoutEffect = _createContext.useContextLayoutEffect,
useContextMutationEffect = _createContext.useContextMutationEffect;
_defineProperty(Provider, "defaultProps", {
initialState: {}
});
exports.Consumer = Consumer;
exports.Container = Container;
exports.createContext = createContext;
exports.Context = Context;
exports.Provider = Provider;
exports.useContextReducer = useContextReducer;
exports.useContextState = useContextState;
exports.useContextEffect = useContextEffect;
exports.useContextLayoutEffect = useContextLayoutEffect;
exports.useContextMutationEffect = useContextMutationEffect;

@@ -1,481 +0,211 @@

import { Component, createElement, createContext } from 'react';
import * as React from 'react';
import { createContext, useState, useMemo, createElement, useRef, useMutationEffect, useContext, useReducer, useEffect } from 'react';
var Context = createContext({
state: {}
});
var hashMap = {};
function hash(string) {
if (hashMap[string] !== undefined) {
return hashMap[string];
}
var Consumer = Context.Consumer;
var _Object$keys = Object.keys(hashMap),
length = _Object$keys.length;
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;
hashMap[string] = 1 << length;
return hashMap[string];
}
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;
function parseUpdater(state, prevState) {
var isUpdater = function isUpdater(a) {
return typeof a === "function";
};
return _extends.apply(this, arguments);
return isUpdater(state) ? state(prevState) : state;
}
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]);
});
function createUseContextEffect(type) {
if (type === void 0) {
type = "useEffect";
}
return target;
}
var consumers = {};
return function (contextKey, create, inputs) {
var key = contextKey;
var consumer = useRef(null);
function _inheritsLoose(subClass, superClass) {
subClass.prototype = Object.create(superClass.prototype);
subClass.prototype.constructor = subClass;
subClass.__proto__ = superClass;
}
if (consumers[key] == null) {
consumers[key] = consumer;
}
function _objectWithoutPropertiesLoose(source, excluded) {
if (source == null) return {};
var target = {};
var sourceKeys = Object.keys(source);
var key, i;
useMutationEffect(function () {
if (!key) return undefined;
return function () {
consumers[key] = null;
};
}, [key]);
React[type](function () {
if (!key || consumers[key] === consumer) {
return create();
}
for (i = 0; i < sourceKeys.length; i++) {
key = sourceKeys[i];
if (excluded.indexOf(key) >= 0) continue;
target[key] = source[key];
}
return target;
return undefined;
}, inputs ? [key].concat(inputs) : undefined);
};
}
function _assertThisInitialized(self) {
if (self === void 0) {
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
}
var EmptyContext = createContext([]);
return self;
}
function createUseContextReducer(context) {
return function (contextKey, reducer, initialState, initialAction) {
var observedBits = contextKey ? hash(contextKey) : undefined; // @ts-ignore
var keys = Object.keys;
function mapSetStateToActions(setState, actionMap) {
return keys(actionMap).reduce(function (map, type) {
var _Object$assign;
var _React$useContext = useContext(contextKey ? context : EmptyContext, observedBits),
contextState = _React$useContext[0],
setContextState = _React$useContext[1];
return Object.assign(map, (_Object$assign = {}, _Object$assign[type] = function () {
return setState(actionMap[type].apply(actionMap, arguments), undefined, type);
}, _Object$assign));
}, {});
}
function mapStateToSelectors(state, selectorMap) {
return keys(selectorMap).reduce(function (map, type) {
var _Object$assign2;
var _React$useReducer = useReducer(reducer, initialState, contextKey ? undefined : initialAction),
state = _React$useReducer[0],
dispatch = _React$useReducer[1];
return Object.assign(map, (_Object$assign2 = {}, _Object$assign2[type] = function () {
return selectorMap[type].apply(selectorMap, arguments)(state);
}, _Object$assign2));
}, {});
}
function mapPropsToEffects(getEffectProps, effectMap) {
return keys(effectMap).reduce(function (final, type) {
var _Object$assign3;
if (contextKey) {
if (contextState[contextKey] != null) {
state = contextState[contextKey];
}
return Object.assign(final, (_Object$assign3 = {}, _Object$assign3[type] = function () {
return effectMap[type].apply(effectMap, arguments)(getEffectProps(type));
}, _Object$assign3));
}, {});
}
function parseUpdater(updaterOrState, state) {
return typeof updaterOrState === "function" ? updaterOrState(state) : updaterOrState;
}
dispatch = function dispatch(action) {
return setContextState(function (prevState) {
var _Object$assign;
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);
}
return Object.assign({}, prevState, (_Object$assign = {}, _Object$assign[contextKey] = reducer(prevState[contextKey], action), _Object$assign));
});
};
});
_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;
}
useMutationEffect(function () {
if (contextKey && contextState[contextKey] == null && (initialState != null || initialAction)) {
setContextState(function (prevState) {
if (prevState[contextKey] == null) {
var _Object$assign2;
var _proto = InnerContainer.prototype;
return Object.assign({}, prevState, (_Object$assign2 = {}, _Object$assign2[contextKey] = initialAction ? reducer(state, initialAction) : state, _Object$assign2));
}
_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 _this$props5 = this.props,
pure = _this$props5.pure,
context = _this$props5.context,
stateFromProps = _this$props5.state;
var nextStateFromProps = nextProps.state,
shouldUpdate = nextProps.shouldUpdate;
var couldUpdate = true;
if (context && stateFromProps && nextStateFromProps) {
couldUpdate = !pure || stateFromProps !== nextStateFromProps;
if (couldUpdate && shouldUpdate) {
couldUpdate = shouldUpdate({
state: stateFromProps,
nextState: nextStateFromProps
return prevState;
});
this.ignoreState = !couldUpdate && nextStateFromProps;
}
} else if (!context) {
couldUpdate = !pure || this.state !== nextState;
if (couldUpdate && shouldUpdate) {
couldUpdate = shouldUpdate({
state: this.state,
nextState: nextState
});
this.ignoreState = !couldUpdate && nextState;
}
}
return couldUpdate;
}, [contextKey]);
return [state, dispatch];
};
}
_proto.componentWillUnmount = function componentWillUnmount() {
var _this3 = this;
function basicStateReducer(state, action) {
return parseUpdater(action, state);
}
var _this$props6 = this.props,
context = _this$props6.context,
onUnmount = _this$props6.onUnmount;
var unmount = function unmount() {
return onUnmount && onUnmount(_this3.getEffectProps("onUnmount"));
};
if (this.unmount) {
this.unmount(unmount);
} else if (!context) {
unmount();
}
function createUseContextState(context) {
var useContextReducer = createUseContextReducer(context);
return function (contextKey, initialState) {
return useContextReducer(contextKey, basicStateReducer, initialState);
};
}
_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);
};
var devtoolsExtension = typeof window !== "undefined" && window.__REDUX_DEVTOOLS_EXTENSION__;
return InnerContainer;
}(Component); // eslint-disable-next-line react/prefer-stateless-function, react/no-multi-comp
function useDevtools(state, setState, _temp) {
var _ref = _temp === void 0 ? {} : _temp,
name = _ref.name,
_ref$enabled = _ref.enabled,
enabled = _ref$enabled === void 0 ? true : _ref$enabled;
_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 _this4 = 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({}, _this4.props, {
state: state[context],
setContextState: setContextState,
mountContainer: mountContainer
}));
var devtools = useRef(null);
var prevState = useRef(null);
useEffect(function () {
if (enabled && devtoolsExtension) {
devtools.current = devtoolsExtension.connect({
name: name
});
}
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);
devtools.current.init(state);
devtools.current.subscribe(function (message) {
if (message.type === "DISPATCH" && message.state) {
setState(JSON.parse(message.state));
}
if (callback) callback(); // istanbul ignore next
if (_this.devtools && type) {
var devtoolsType = context ? context + "/" + type : type;
_this.devtools.send(devtoolsType, _this.state.state);
});
return function () {
if (devtools.current) {
devtools.current.unsubscribe();
devtoolsExtension.disconnect();
}
}; // @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
return undefined;
}, [enabled]);
useEffect(function () {
if (enabled && devtoolsExtension) {
var changedKey;
if (devtools && reduxDevtoolsExtension) {
_this.devtools = reduxDevtoolsExtension.connect({
name: "Context"
});
for (var key in state) {
if (prevState.current && state[key] !== prevState.current[key]) {
changedKey = key;
}
}
_this.devtools.init(initialState);
if (changedKey && devtools.current) {
devtools.current.send(changedKey, state);
}
_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))
};
});
}
});
prevState.current = state;
}
}, [state, enabled]);
}
return _this;
}
function createContext$1(initialState, name) {
var Context = createContext([initialState, function () {}], function (_ref, _ref2) {
var prev = _ref[0];
var next = _ref2[0];
var changedBits = 0;
var _proto = Provider.prototype;
_proto.componentDidMount = function componentDidMount() {
if (this.props.onMount) {
this.props.onMount(this.getProps("Provider/onMount"));
for (var contextKey in next) {
if (prev[contextKey] !== next[contextKey]) {
changedBits |= hash(contextKey);
}
}
};
_proto.componentWillUnmount = function componentWillUnmount() {
if (this.props.onUnmount) {
var _this$getProps = this.getProps(),
_setContextState2 = _this$getProps.setContextState,
args = _objectWithoutPropertiesLoose(_this$getProps, ["setContextState"]);
return changedBits;
});
this.props.onUnmount(args);
} // istanbul ignore next
if (this.devtools && reduxDevtoolsExtension) {
this.devtools.unsubscribe();
reduxDevtoolsExtension.disconnect();
}
var Provider = function Provider(_ref3) {
var children = _ref3.children,
devtools = _ref3.devtools;
var state = useState(initialState);
var value = useMemo(function () {
return state;
}, [state[0]]);
useDevtools(state[0], state[1], {
name: name,
enabled: devtools
});
return createElement(Context.Provider, {
value: value
}, children);
};
_proto.render = function render() {
return (// @ts-ignore
createElement(Context.Provider, {
value: this.state
}, this.props.children)
);
return {
Context: Context,
Provider: Provider,
useContextState: createUseContextState(Context),
useContextReducer: createUseContextReducer(Context),
useContextEffect: createUseContextEffect(),
useContextLayoutEffect: createUseContextEffect("useLayoutEffect"),
useContextMutationEffect: createUseContextEffect("useMutationEffect")
};
}
return Provider;
}(Component);
var _createContext = createContext$1({}),
Context = _createContext.Context,
Provider = _createContext.Provider,
useContextReducer = _createContext.useContextReducer,
useContextState = _createContext.useContextState,
useContextEffect = _createContext.useContextEffect,
useContextLayoutEffect = _createContext.useContextLayoutEffect,
useContextMutationEffect = _createContext.useContextMutationEffect;
_defineProperty(Provider, "defaultProps", {
initialState: {}
});
export { Consumer, Container, Provider };
export { createContext$1 as createContext, Context, Provider, useContextReducer, useContextState, useContextEffect, useContextLayoutEffect, useContextMutationEffect };

@@ -1,1 +0,1 @@

!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("react")):"function"==typeof define&&define.amd?define(["exports","react"],e):e(t.constate={},t.React)}(this,function(t,s){"use strict";var n=s.createContext({state:{}}),o=n.Consumer;function u(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}function a(){return(a=Object.assign||function(t){for(var e=1;e<arguments.length;e++){var n=arguments[e];for(var o in n)Object.prototype.hasOwnProperty.call(n,o)&&(t[o]=n[o])}return t}).apply(this,arguments)}function p(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{},o=Object.keys(n);"function"==typeof Object.getOwnPropertySymbols&&(o=o.concat(Object.getOwnPropertySymbols(n).filter(function(t){return Object.getOwnPropertyDescriptor(n,t).enumerable}))),o.forEach(function(t){u(e,t,n[t])})}return e}function r(t,e){t.prototype=Object.create(e.prototype),(t.prototype.constructor=t).__proto__=e}function f(t){if(void 0===t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return t}var l=Object.keys;function d(t,e){return"function"==typeof t?t(e):t}var c=function(i){function t(t){var c;u(f(f(c=i.call(this,t)||this)),"state",c.props.initialState),u(f(f(c)),"ignoreState",!1),u(f(f(c)),"unmount",void 0),u(f(f(c)),"getEffectProps",function(n){var t=c.props,e=t.context,o=t.state;return{state:e&&o?o:c.state,setState:function(t,e){return c.handleSetState(t,e,n)}}}),u(f(f(c)),"handleSetState",function(e,r,i){var s,t=c.props,n=t.context,o=t.setContextState,a=function(t){return d(e,s=t)},u=function(){var t=c.props,e=t.state,n=void 0===e?c.state:e,o=t.onUpdate;o&&c.ignoreState!==n&&o(p({},c.getEffectProps("onUpdate"),{prevState:s,type:i})),r&&r()};n&&o?o(n,a,u,i):c.setState(a,u)});var e=t.context,n=t.setContextState,o=t.state,r=t.initialState;return e&&n&&!o&&n(e,function(t){return Object.assign({},r,t)},void 0,"initialState"),c}r(t,i);var e=t.prototype;return e.componentDidMount=function(){var t=this,e=this.props,n=e.context,o=e.mountContainer,r=e.onMount,i=function(){return r&&r(t.getEffectProps("onMount"))};n&&o?this.unmount=o(n,i):n||i()},e.shouldComponentUpdate=function(t,e){var n=this.props,o=n.pure,r=n.context,i=n.state,s=t.state,a=t.shouldUpdate,u=!0;return r&&i&&s?(u=!o||i!==s)&&a&&(u=a({state:i,nextState:s}),this.ignoreState=!u&&s):r||(u=!o||this.state!==e)&&a&&(u=a({state:this.state,nextState:e}),this.ignoreState=!u&&e),u},e.componentWillUnmount=function(){var t=this,e=this.props,n=e.context,o=e.onUnmount,r=function(){return o&&o(t.getEffectProps("onUnmount"))};this.unmount?this.unmount(r):n||r()},e.render=function(){var o,r,i,s,a,u,t=this.props,e=t.state,n=t.children,c=t.actions,p=t.selectors,f=t.effects;return n(Object.assign({},e||this.state,c&&(a=this.handleSetState,l(u=c).reduce(function(t,e){var n;return Object.assign(t,((n={})[e]=function(){return a(u[e].apply(u,arguments),void 0,e)},n))},{})),p&&(i=e||this.state,l(s=p).reduce(function(t,e){var n;return Object.assign(t,((n={})[e]=function(){return s[e].apply(s,arguments)(i)},n))},{})),f&&(o=this.getEffectProps,l(r=f).reduce(function(t,e){var n;return Object.assign(t,((n={})[e]=function(){return r[e].apply(r,arguments)(o(e))},n))},{}))))},t}(s.Component);u(c,"defaultProps",{initialState:{}});var e=function(t){function e(){return t.apply(this,arguments)||this}return r(e,t),e.prototype.render=function(){var r=this,i=this.props.context;return void 0!==i?s.createElement(o,null,function(t){var e=t.state,n=t.setContextState,o=t.mountContainer;return s.createElement(c,a({},r.props,{state:e[i],setContextState:n,mountContainer:o}))}):s.createElement(c,this.props)},e}(s.Component),i="undefined"!=typeof window&&window.__REDUX_DEVTOOLS_EXTENSION__,v=function(o){function t(t){var a;u(f(f(a=o.call(this,t)||this)),"containers",{}),u(f(f(a)),"devtools",void 0),u(f(f(a)),"mountContainer",function(e,t){return a.containers[e]||(a.containers[e]=0,t&&a.setState(null,t)),a.containers[e]+=1,function(t){1===a.containers[e]&&t&&t(),a.containers[e]-=1}}),u(f(f(a)),"setContextState",function(n,o,r,i){var s;a.setState(function(t){var e;return s=t.state,{state:Object.assign({},t.state,(e={},e[n]=Object.assign({},t.state[n],d(o,t.state[n]||{})),e))}},function(){if(a.props.onUpdate){var t=p({},a.getProps("Provider/onUpdate"),{prevState:s,context:n,type:i});a.props.onUpdate(t)}if(r&&r(),a.devtools&&i){var e=n?n+"/"+i:i;a.devtools.send(e,a.state.state)}})}),u(f(f(a)),"getProps",function(o){var t=a.state,e=t.state,r=t.setContextState;return{state:e,setContextState:function(o){function t(t,e,n){return o.apply(this,arguments)}return t.toString=function(){return o.toString()},t}(function(t,e,n){return r(t,e,n,o)})}});var e=t.devtools,n=t.initialState;return a.state={state:n,mountContainer:a.mountContainer,setContextState:a.setContextState},e&&i&&(a.devtools=i.connect({name:"Context"}),a.devtools.init(n),a.devtools.subscribe(function(e){"DISPATCH"===e.type&&e.state&&a.setState(function(t){return{state:Object.assign({},t.state,JSON.parse(e.state))}})})),a}r(t,o);var e=t.prototype;return e.componentDidMount=function(){this.props.onMount&&this.props.onMount(this.getProps("Provider/onMount"))},e.componentWillUnmount=function(){if(this.props.onUnmount){var t=this.getProps(),e=(t.setContextState,function(t,e){if(null==t)return{};var n,o,r={},i=Object.keys(t);for(o=0;o<i.length;o++)n=i[o],0<=e.indexOf(n)||(r[n]=t[n]);return r}(t,["setContextState"]));this.props.onUnmount(e)}this.devtools&&i&&(this.devtools.unsubscribe(),i.disconnect())},e.render=function(){return s.createElement(n.Provider,{value:this.state},this.props.children)},t}(s.Component);u(v,"defaultProps",{initialState:{}}),t.Consumer=o,t.Container=e,t.Provider=v,Object.defineProperty(t,"__esModule",{value:!0})});
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("react")):"function"==typeof define&&define.amd?define(["exports","react"],t):t(e.constate={},e.React)}(this,function(e,y){"use strict";var n={};function v(e){if(void 0!==n[e])return n[e];var t=Object.keys(n).length;return n[e]=1<<t,n[e]}function t(o){void 0===o&&(o="useEffect");var c={};return function(e,t,n){var u=e,r=y.useRef(null);null==c[u]&&(c[u]=r),y.useMutationEffect(function(){if(u)return function(){c[u]=null}},[u]),y[o](function(){if(!u||c[u]===r)return t()},n?[u].concat(n):void 0)}}var l=y.createContext([]);function u(d){return function(u,r,e,n){var t=u?v(u):void 0,o=y.useContext(u?d:l,t),c=o[0],f=o[1],i=y.useReducer(r,e,u?void 0:n),a=i[0],s=i[1];return u&&(null!=c[u]&&(a=c[u]),s=function(n){return f(function(e){var t;return Object.assign({},e,((t={})[u]=r(e[u],n),t))})}),y.useMutationEffect(function(){u&&null==c[u]&&(null!=e||n)&&f(function(e){return null!=e[u]?e:Object.assign({},e,((t={})[u]=n?r(a,n):a,t));var t})},[u]),[a,s]}}function r(e,t){return u=e,"function"==typeof(n=t)?n(u):n;var n,u}var b="undefined"!=typeof window&&window.__REDUX_DEVTOOLS_EXTENSION__;function o(x,C){var e,n,E=y.createContext([x,function(){}],function(e,t){var n=e[0],u=t[0],r=0;for(var o in u)n[o]!==u[o]&&(r|=v(o));return r});return{Context:E,Provider:function(e){var n,t,u,r,o,c,f,i,a,s=e.children,d=e.devtools,v=y.useState(x),l=y.useMemo(function(){return v},[v[0]]);return n=v[0],t=v[1],o=(r=void 0===(u={name:C,enabled:d})?{}:u).name,c=r.enabled,f=void 0===c||c,i=y.useRef(null),a=y.useRef(null),y.useEffect(function(){if(f&&b)return i.current=b.connect({name:o}),i.current.init(n),i.current.subscribe(function(e){"DISPATCH"===e.type&&e.state&&t(JSON.parse(e.state))}),function(){i.current&&(i.current.unsubscribe(),b.disconnect())}},[f]),y.useEffect(function(){if(f&&b){var e;for(var t in n)a.current&&n[t]!==a.current[t]&&(e=t);e&&i.current&&i.current.send(e,n),a.current=n}},[n,f]),y.createElement(E.Provider,{value:l},s)},useContextState:(e=E,n=u(e),function(e,t){return n(e,r,t)}),useContextReducer:u(E),useContextEffect:t(),useContextLayoutEffect:t("useLayoutEffect"),useContextMutationEffect:t("useMutationEffect")}}var c=o({}),f=c.Context,i=c.Provider,a=c.useContextReducer,s=c.useContextState,d=c.useContextEffect,x=c.useContextLayoutEffect,C=c.useContextMutationEffect;e.createContext=o,e.Context=f,e.Provider=i,e.useContextReducer=a,e.useContextState=s,e.useContextEffect=d,e.useContextLayoutEffect=x,e.useContextMutationEffect=C,Object.defineProperty(e,"__esModule",{value:!0})});

@@ -1,5 +0,12 @@

import Consumer from "./Consumer";
import Container from "./Container";
import Provider from "./Provider";
export * from "./types";
export { Consumer, Container, Provider };
/// <reference types="react" />
import createContext from "./createContext";
declare const Context: import("react").Context<[{
[key: string]: any;
}, import("./types").SetState<{
[key: string]: any;
}>]>, Provider: ({ children, devtools }: import("./createContext").ProviderProps) => JSX.Element, useContextReducer: import("./createUseContextReducer").UseContextReducer<{
[key: string]: any;
}>, useContextState: import("./createUseContextState").UseContextState<{
[key: string]: any;
}>, useContextEffect: (contextKey: string | number | null | undefined, create: () => void | (() => void), inputs?: ReadonlyArray<any> | undefined) => void, useContextLayoutEffect: (contextKey: string | number | null | undefined, create: () => void | (() => void), inputs?: ReadonlyArray<any> | undefined) => void, useContextMutationEffect: (contextKey: string | number | null | undefined, create: () => void | (() => void), inputs?: ReadonlyArray<any> | undefined) => void;
export { createContext, Context, Provider, useContextReducer, useContextState, useContextEffect, useContextLayoutEffect, useContextMutationEffect };

@@ -1,101 +0,5 @@

import * as React from "react";
export declare type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
export declare type EventKeys = "onMount" | "onUpdate" | "onUnmount" | "initialState";
export declare type StateUpdater<S> = (state: Readonly<S>) => Partial<S>;
export declare type StateCallback = () => void;
export declare type SetState<S> = (updaterOrState: StateUpdater<S> | Partial<S>, callback?: () => void) => void;
export declare type SetStateWithType<S, K> = (updaterOrState: StateUpdater<S> | Partial<S>, callback: StateCallback | undefined, type: K) => void;
export declare type SetContextState<S, K> = (context: string, updaterOrState: StateUpdater<S> | Partial<S>, callback?: StateCallback, type?: K) => void;
export declare type Action<S, T> = T extends (...args: infer U) => any ? (...args: U) => StateUpdater<S> : (...args: unknown[]) => StateUpdater<S>;
export declare type Selector<S, T> = T extends (...args: infer U) => infer R ? (...args: U) => (state: Readonly<S>) => R : (...args: unknown[]) => (state: Readonly<S>) => any;
export interface EffectProps<S> {
state: Readonly<S>;
setState: SetState<S>;
}
export declare type Effect<S, T> = T extends (...args: infer U) => infer R ? (...args: U) => (props: EffectProps<S>) => R : (...args: unknown[]) => (props: EffectProps<S>) => any;
export declare type ActionMap<S, P> = {
[K in keyof P]: Action<S, P[K]>;
};
export declare type SelectorMap<S, P> = {
[K in keyof P]: Selector<S, P[K]>;
};
export declare type EffectMap<S, P> = {
[K in keyof P]: Effect<S, P[K]>;
};
export interface OnMountProps<S> extends EffectProps<S> {
}
export interface OnMount<S> {
(props: OnMountProps<S>): any;
}
export interface OnUpdateProps<S, AP = {}, EP = {}> extends EffectProps<S> {
prevState: Readonly<S>;
type: keyof AP | keyof EP | EventKeys;
}
export interface OnUpdate<S, AP = {}, EP = {}> {
(props: OnUpdateProps<S, AP, EP>): any;
}
export interface OnUnmountProps<S> extends EffectProps<S> {
}
export interface OnUnmount<S> {
(props: OnUnmountProps<S>): any;
}
export interface ShouldUpdateProps<S> {
state: Readonly<S>;
nextState: Readonly<S>;
}
export interface ShouldUpdate<S> {
(props: ShouldUpdateProps<S>): boolean;
}
export interface ProviderOnMountProps<S> {
state: S;
setContextState: SetContextState<S[keyof S], string>;
}
export interface ProviderOnMount<S> {
(props: ProviderOnMountProps<S>): any;
}
export interface ProviderOnUpdateProps<S> {
prevState: S;
state: S;
setContextState: SetContextState<S[keyof S], string>;
context: string;
type?: string;
}
export interface ProviderOnUpdate<S> {
(props: ProviderOnUpdateProps<S>): any;
}
export interface ProviderOnUnmountProps<S> {
state: S;
}
export interface ProviderOnUnmount<S> {
(props: ProviderOnUnmountProps<S>): any;
}
export declare type MountContainer = (context: string, onMount?: () => void) => (onUnmount?: () => void) => void;
export interface ContainerProps<S, AP = {}, SP = {}, EP = {}> {
initialState?: Partial<S>;
context?: string;
actions?: ActionMap<S, AP>;
selectors?: SelectorMap<S, SP>;
effects?: EffectMap<S, EP>;
onMount?: OnMount<S>;
onUpdate?: OnUpdate<S, AP, EP>;
onUnmount?: OnUnmount<S>;
shouldUpdate?: ShouldUpdate<S>;
pure?: boolean;
children: (props: S & AP & SP & EP) => React.ReactNode;
}
export interface InnerContainerProps<S, AP = {}, SP = {}, EP = {}> extends ContainerProps<S, AP, SP, EP> {
state?: S;
setContextState?: SetContextState<S, keyof AP | keyof EP | EventKeys>;
mountContainer?: MountContainer;
}
export declare type ComposableContainerProps<S, AP = {}, SP = {}, EP = {}> = Omit<ContainerProps<S, AP, SP, EP>, "actions" | "selectors" | "effects">;
export interface ComposableContainer<S, AP = {}, SP = {}, EP = {}> {
(props: ComposableContainerProps<S, AP, SP, EP>): JSX.Element;
}
export interface ProviderProps<S> {
initialState?: Partial<S>;
devtools?: boolean;
onMount?: ProviderOnMount<S>;
onUpdate?: ProviderOnUpdate<S>;
onUnmount?: ProviderOnUnmount<S>;
}
export declare type StateUpdater<State> = (state: State) => State;
export declare type SetState<State> = (state: State | StateUpdater<State>) => void;
export declare type Reducer<State, Action> = (state: State, action: Action) => State;
export declare type ContextState<State> = [State, SetState<State>];
export declare type ContextReducer<State, Action> = [State, (action: Action) => void];

@@ -1,5 +0,3 @@

import { ActionMap, SelectorMap, EffectMap, StateUpdater, EffectProps, SetStateWithType } from "./types";
export declare function mapSetStateToActions<S, P>(setState: SetStateWithType<S, keyof P>, actionMap: ActionMap<S, P>): P;
export declare function mapStateToSelectors<S, P>(state: S, selectorMap: SelectorMap<S, P>): P;
export declare function mapPropsToEffects<S, P>(getEffectProps: (type: keyof P) => EffectProps<S>, effectMap: EffectMap<S, P>): P;
export declare function parseUpdater<S>(updaterOrState: StateUpdater<S> | Partial<S>, state: S): Partial<S>;
import { StateUpdater } from "./types";
export declare function hash(string: string): number;
export declare function parseUpdater<S>(state: StateUpdater<S> | S, prevState: S): S;
{
"name": "constate",
"version": "0.9.0",
"version": "1.0.0-alpha.0",
"description": "Yet another React state management library that lets you work with local state and scale up to global state with ease",

@@ -22,3 +22,2 @@ "license": "MIT",

"scripts": {
"start": "parcel examples/index.html",
"test": "jest",

@@ -28,9 +27,14 @@ "coverage": "npm test -- --coverage",

"type-check": "tsc --noEmit",
"lint": "eslint src test examples --ext js,ts,tsx",
"lint": "eslint . --ext js,ts,tsx",
"clean": "rimraf dist",
"prebuild": "npm run clean",
"build": "tsc --emitDeclarationOnly && rollup -c",
"preversion": "npm run lint && npm test && npm run build",
"version": "standard-changelog && git add CHANGELOG.md",
"postpublish": "git push origin master --follow-tags"
"prerelease": "npm run lint && npm test && npm run build",
"release": "standard-version",
"postpublish": "git push origin master --follow-tags",
"prepare": "npm run examples:install",
"examples:install": "yarn --cwd examples",
"examples:start": "npm run start --prefix examples",
"examples:build": "npm run build --prefix examples",
"examples:upgrade": "yarn upgrade-interactive --latest --cwd examples"
},

@@ -51,58 +55,47 @@ "husky": {

],
"dependencies": {},
"devDependencies": {
"@babel/cli": "^7.1.2",
"@babel/core": "^7.1.2",
"@babel/plugin-proposal-class-properties": "^7.1.0",
"@babel/plugin-proposal-object-rest-spread": "^7.0.0",
"@babel/preset-env": "^7.1.0",
"@babel/cli": "^7.1.5",
"@babel/core": "^7.1.6",
"@babel/preset-env": "^7.1.6",
"@babel/preset-react": "^7.0.0",
"@babel/preset-typescript": "^7.1.0",
"@types/enzyme": "^3.1.14",
"@types/enzyme-adapter-react-16": "^1.0.3",
"@types/jest": "^23.3.2",
"@types/jest": "^23.3.9",
"@types/prop-types": "^15.5.6",
"@types/react": "^16.4.14",
"@types/react": "^16.7.6",
"babel-core": "^7.0.0-0",
"babel-eslint": "^10.0.1",
"babel-jest": "^23.6.0",
"babel-plugin-transform-es2015-modules-commonjs": "^6.26.0",
"enzyme": "^3.6.0",
"enzyme-adapter-react-16": "^1.5.0",
"eslint": "^5.6.1",
"eslint": "^5.9.0",
"eslint-config-airbnb": "^17.1.0",
"eslint-config-prettier": "^3.1.0",
"eslint-import-resolver-typescript": "^1.0.2",
"eslint-config-prettier": "^3.3.0",
"eslint-plugin-import": "^2.14.0",
"eslint-plugin-jsx-a11y": "^6.1.1",
"eslint-plugin-jsx-a11y": "^6.1.2",
"eslint-plugin-prettier": "^3.0.0",
"eslint-plugin-react": "^7.11.1",
"eslint-plugin-typescript": "^0.12.0",
"husky": "^1.1.0",
"eslint-plugin-typescript": "^0.13.0",
"husky": "^1.1.4",
"jest-cli": "^23.6.0",
"lint-staged": "^7.3.0",
"opn-cli": "^3.1.0",
"parcel-bundler": "^1.10.1",
"prettier": "^1.14.3",
"raf": "^3.4.0",
"react": "^16.5.2",
"react-dom": "^16.5.2",
"react-router-dom": "^4.3.1",
"react-test-renderer": "^16.5.2",
"lint-staged": "^8.0.5",
"opn-cli": "^4.0.0",
"prettier": "^1.15.2",
"raf": "^3.4.1",
"react": "^16.7.0-alpha.2",
"react-dom": "^16.7.0-alpha.2",
"react-test-renderer": "^16.6.3",
"react-testing-library": "^5.2.3",
"rimraf": "^2.6.2",
"rollup": "^0.66.2",
"rollup": "^0.67.1",
"rollup-plugin-babel": "^4.0.3",
"rollup-plugin-commonjs": "^9.1.8",
"rollup-plugin-filesize": "^4.0.1",
"rollup-plugin-ignore": "^1.0.3",
"rollup-plugin-commonjs": "^9.2.0",
"rollup-plugin-ignore": "^1.0.4",
"rollup-plugin-node-resolve": "^3.4.0",
"rollup-plugin-replace": "^2.0.0",
"rollup-plugin-replace": "^2.1.0",
"rollup-plugin-uglify": "^6.0.0",
"standard-changelog": "^2.0.1",
"typescript": "^3.1.1",
"typescript-eslint-parser": "^19.0.2"
"standard-version": "^4.4.0",
"typescript": "^3.1.6",
"typescript-eslint-parser": "^21.0.1"
},
"peerDependencies": {
"react": "^16.3.0"
"react": "^16.7.0-alpha.2"
}
}
<p align="center">
<img src="https://raw.githubusercontent.com/diegohaz/constate/master/logo/logo.png" alt="constate logo" width="300" />
</p>
<br /><br />
<br>
<p align="center">
<a href="https://github.com/diegohaz/nod"><img alt="Generated with nod" src="https://img.shields.io/badge/generator-nod-2196F3.svg?style=flat-square" /></a>
<a href="https://npmjs.org/package/constate"><img alt="NPM version" src="https://img.shields.io/npm/v/constate.svg?style=flat-square" /></a>
<a href="https://unpkg.com/constate"><img alt="Gzip size" src="https://img.badgesize.io/https://unpkg.com/constate?style=flat-square&compression=gzip" /></a>
<a href="https://david-dm.org/diegohaz/constate"><img alt="Dependencies" src="https://img.shields.io/david/diegohaz/constate/master.svg?style=flat-square" /></a>
<a href="https://travis-ci.org/diegohaz/constate"><img alt="Build Status" src="https://img.shields.io/travis/diegohaz/constate/master.svg?style=flat-square" /></a>
<a href="https://codecov.io/gh/diegohaz/constate/branch/master"><img alt="Coverage Status" src="https://img.shields.io/codecov/c/github/diegohaz/constate/master.svg?style=flat-square" /></a>
1 kB React state management library that lets you write contextual state<br>
as if it were local state, using <a href="https://reactjs.org/docs/hooks-intro.html">React Hooks</a>.
</p>
<br /><br />
> context + state = constate
<br>
React state management library built with scalability in mind. You can start simple with [local state](#actions) and scale up to [global state](#context) with ease when needed.
<p align="center">
<a href="https://npmjs.org/package/constate"><img alt="NPM version" src="https://img.shields.io/npm/v/constate.svg?style=flat-square"></a>
<a href="https://unpkg.com/constate"><img alt="Gzip size" src="https://img.badgesize.io/https://unpkg.com/constate?style=flat-square&compression=gzip"></a>
<a href="https://david-dm.org/diegohaz/constate"><img alt="Dependencies" src="https://img.shields.io/david/diegohaz/constate/master.svg?style=flat-square"></a>
<a href="https://travis-ci.org/diegohaz/constate"><img alt="Build Status" src="https://img.shields.io/travis/diegohaz/constate/master.svg?style=flat-square"></a>
<a href="https://codecov.io/gh/diegohaz/constate/branch/master"><img alt="Coverage Status" src="https://img.shields.io/codecov/c/github/diegohaz/constate/master.svg?style=flat-square"></a>
</p>
👓 [**Read the introductory article**](https://medium.freecodecamp.org/reacts-new-context-api-how-to-toggle-between-local-and-global-state-c6ace81443d0)<br>
🎮 [**Play with the demo**](https://codesandbox.io/s/7p2qv6mmq)
<br>
<br><br>
<p align="center">
<strong>🎮 Play with CodeSandbox examples</strong>
<br>
<a href="https://codesandbox.io/s/github/diegohaz/constate/tree/master/examples/counter">Counter</a>
</p>
<br>
```jsx
import React from "react";
import { Container } from "constate";
import { Provider, useContextState } from "constate";
const initialState = { count: 0 };
function DecrementButton() {
// const [count, setCount] = React.useState(0);
const [count, setCount] = useContextState("counter1", 0);
const decrement = () => setCount(count - 1)
return <button onClick={decrement}>-</button>;
}
const actions = {
increment: () => state => ({ count: state.count + 1 })
};
function IncrementButton() {
// const [count, setCount] = React.useState(0);
const [count, setCount] = useContextState("counter1", 0);
const increment = () => setCount(count + 1)
return <button onClick={increment}>+</button>;
}
const Counter = () => (
<Container initialState={initialState} actions={actions}>
{({ count, increment }) => (
<button onClick={increment}>{count}</button>
)}
</Container>
);
function Count() {
// const [count] = React.useState(0);
const [count] = useContextState("counter1", 0);
return <span>{count}</span>
}
function App() {
return (
<Provider>
<DecrementButton />
<Count />
<IncrementButton />
</Provider>
);
}
```
<p align="center"><img src="https://user-images.githubusercontent.com/3068563/39095434-ba7c42c2-4616-11e8-9836-f46ea572c170.gif" alt="Example"></p>
<br>
**Table of Contents**
#### Table of Contents
- [Installation](#installation)
- [`Container`](#container)
- [`initialState`](#initialstate)
- [`actions`](#actions)
- [`selectors`](#selectors)
- [`effects`](#effects)
- [`context`](#context)
- [`onMount`](#onmount)
- [`onUpdate`](#onupdate)
- [`onUnmount`](#onunmount)
- [`shouldUpdate`](#shouldupdate)
- [`pure`](#pure)
- [`Provider`](#provider)
- [`initialState`](#initialstate-1)
- [`onMount`](#onmount-1)
- [`onUpdate`](#onupdate-1)
- [`onUnmount`](#onunmount-1)
- [`devtools`](#devtools)
- [TypeScript](#typescript)
- [Testing](#testing)
- [`useContextState`](#usecontextstate)
- [`useContextReducer`](#usecontextreducer)
- [`useContextEffect`](#usecontexteffect)
- [`createContext`](#createcontext)
<br>
## Installation
```sh
npm i constate
npm i constate@next
```
## `Container`
<br>
> In computer science, a **container** is a class, a data structure, or an abstract data type (ADT) whose instances are collections of other objects. In other words, they store objects in an organized way that follows specific access rules.
>
> — <https://en.wikipedia.org/wiki/Container_(abstract_data_type)>
## `Provider`
### `initialState`
<sup><a href="#table-of-contents">↑ Back to top</a></sup>
```ts
type initialState = object;
```
First, you should wrap your app (or the part using Constate) with `Provider` so as to access contextual state within hooks:
Use this prop to define the initial state of the container.
```jsx
const initialState = { count: 0 };
import React from "react";
import { Provider } from "constate";
const Counter = () => (
<Container initialState={initialState}>
{({ count }) => <button>{count}</button>}
</Container>
);
function App() {
return (
<Provider devtools={process.env.NODE_ENV === "development"}>
...
</Provider>
);
}
```
<p align="center"><img src="https://user-images.githubusercontent.com/3068563/40029572-206945be-579a-11e8-95ad-4613adf73da4.png" width="55" alt="Example"></p>
Passing `devtools` prop to `Provider` will enable the [redux-devtools-extension](https://github.com/zalmoxisus/redux-devtools-extension) integration, if that's installed in your browser. With that, you can easily debug the state of your application.
### `actions`
<p align="center">
<img src="https://user-images.githubusercontent.com/3068563/48814011-62601300-ed20-11e8-896b-c5c7a080989e.png" alt="Using Redux Devtools Extension" width="800">
</p>
```ts
type Actions = {
[key: string]: (...args: any[]) =>
((state: object) => object) | object
};
```
<br>
An action is a method that returns an `updater` function, which will be, internally, passed as an argument to React `setState`. Actions will be exposed, then, together with state within the child function.
## `useContextState`
You can also return the object directly if you don't need `state`.
<sup><a href="#table-of-contents">↑ Back to top</a></sup>
```jsx
const initialState = { count: 0 };
`useContextState` has the same API as [`React.useState`](https://reactjs.org/docs/hooks-reference.html#usestate), except that it receives `contextKey` as the first argument.
const actions = {
increment: amount => state => ({ count: state.count + amount })
};
const Counter = () => (
<Container initialState={initialState} actions={actions}>
{({ count, increment }) => (
<button onClick={() => increment(1)}>{count}</button>
)}
</Container>
);
```
<p align="center"><img src="https://user-images.githubusercontent.com/3068563/39095434-ba7c42c2-4616-11e8-9836-f46ea572c170.gif" alt="Example"></p>
### `selectors`
```ts
type Selectors = {
[key: string]: (...args: any[]) =>
(state: object) => any
};
```
A selector is a method that returns a function, which receives the current state and should return something (the thing being selected).
```jsx
const initialState = { count: 0 };
import { useContextState } from "constate";
const actions = {
increment: amount => state => ({ count: state.count + amount })
};
const selectors = {
getParity: () => state => (state.count % 2 === 0 ? "even" : "odd")
};
const Counter = () => (
<Container
initialState={initialState}
actions={actions}
selectors={selectors}
>
{({ count, increment, getParity }) => (
<button onClick={() => increment(1)}>{count} {getParity()}</button>
)}
</Container>
);
function Component() {
// accesses state.contextKey in context
const [state, setState] = useContextState("contextKey", "initialValue");
...
}
```
<p align="center"><img src="https://user-images.githubusercontent.com/3068563/39095320-f1595764-4614-11e8-98e3-343042cef61c.gif" alt="Example"></p>
If you pass `null` or `undefined` into the `contextKey` parameter, it'll work exactly like [`React.useState`](https://reactjs.org/docs/hooks-reference.html#usestate):
### `effects`
```ts
type Effects = {
[key: string]: (...args: any[]) =>
(props: { state: object, setState: Function }) => void
};
```
An effect is a method that returns a function, which receives both `state` and `setState`. This is useful if you need to perform side effects, like async actions, or just want to use `setState`.
```jsx
const initialState = { count: 0 };
import { useContextState } from "constate";
const effects = {
tick: () => ({ setState }) => {
const fn = () => setState(state => ({ count: state.count + 1 }));
setInterval(fn, 1000);
}
};
const Counter = () => (
<Container initialState={initialState} effects={effects}>
{({ count, tick }) => (
<button onClick={tick}>{count}</button>
)}
</Container>
);
function Component() {
// same as React.useState("initialValue")
const [state, setState] = useContextState(null, "initialValue");
...
}
```
<p align="center"><img src="https://user-images.githubusercontent.com/3068563/39095395-46d82eb2-4616-11e8-9e15-e5bb5041b4a8.gif" alt="Example"></p>
This means you can create [custom hooks](https://reactjs.org/docs/hooks-custom.html) that can be either contextual or local depending on the component using it:
### `context`
```ts
type Context = string;
```
Whenever you need to share state between components and/or feel the need to have a global state, you can pass a `context` prop to `Container` and wrap your app with `Provider`.
> Due to how React Context works, `Container`s with `context` prop will re-render on every context state change. It's recommended to use [`pure`](#pure) or [`shouldUpdate`](#shouldupdate) so as to avoid unnecessary re-renders.
```jsx
import { Provider, Container } from "constate";
import React from "react";
import { useContextState } from "constate";
const CounterContainer = props => (
<Container
initialState={{ count: 0 }}
actions={{ increment: () => state => ({ count: state.count + 1 }) }}
{...props}
/>
);
function useCounter(context) {
const [count, setCount] = useContextState(context, 0);
const increment = () => setCount(count + 1);
return { count, increment };
}
const CounterButton = () => (
<CounterContainer context="counter1" pure>
{({ increment }) => <button onClick={increment}>Increment</button>}
</CounterContainer>
);
function ContextualCounter() {
const { count, increment } = useCounter("counter1");
return <button onClick={increment}>{count}</button>;
}
const CounterValue = () => (
<CounterContainer context="counter1" pure>
{({ count }) => <div>{count}</div>}
</CounterContainer>
);
const App = () => (
<Provider>
<CounterButton />
<CounterValue />
</Provider>
);
function LocalCounter() {
const { count, increment } = useCounter();
return <button onClick={increment}>{count}</button>;
}
```
<p align="center"><img src="https://user-images.githubusercontent.com/3068563/39095299-b176af2a-4614-11e8-99ce-4980bdf2f139.gif" alt="Example"></p>
<br>
### `onMount`
## `useContextReducer`
```ts
type OnMount = (props: {
state: object,
setState: Function
}) => void;
```
<sup><a href="#table-of-contents">↑ Back to top</a></sup>
This is a function called inside `Container`'s `componentDidMount`.
Just like [`useContextState`](#usecontextstate), `useContextReducer` works similarly to [`React.useReducer`](https://reactjs.org/docs/hooks-reference.html#usereducer), but accepting a `contextKey` argument:
> Note: when using [`context`](#context), all `Container`s of the same context behave as a single unit, which means that `onMount` will be called only for the first mounted `Container` of each context.
```jsx
const initialState = { count: 0 };
import { useContextReducer } from "constate";
const onMount = ({ setState }) => {
const fn = () => setState(state => ({ count: state.count + 1 }));
document.body.addEventListener("mousemove", fn);
};
const Counter = () => (
<Container initialState={initialState} onMount={onMount}>
{({ count }) => <button>{count}</button>}
</Container>
);
```
<p align="center"><img src="https://user-images.githubusercontent.com/3068563/40272346-be92ffb8-5b79-11e8-906f-a653e284002e.gif" alt="Example"></p>
### `onUpdate`
```ts
type OnUpdate = (props: {
prevState: object,
state: object,
setState: Function,
type: string
}) => void;
```
This is a function called every time `setState` is called, either internally with [`actions`](#actions) or directly with [`effects`](#effects) and lifecycle methods, including `onUpdate` itself.
Besides `prevState`, `state` and `setState`, it receives a `type` property, which can be either the name of the `action`, `effect` or one of the lifecycle methods that triggered it, including `onUpdate` itself.
> Note: when using [`context`](#context), `onUpdate` will be triggered only once per `setState` call no matter how many `Container`s of the same context you have mounted.
```jsx
const initialState = { count: 0 };
const onMount = ({ setState }) => {
const fn = () => setState(state => ({ count: state.count + 1 }));
setInterval(fn, 1000);
};
const onUpdate = ({ state, setState, type }) => {
if (type === "onMount" && state.count === 5) {
// reset counter
setState({ count: 0 });
function reducer(state, action) {
switch(action.type) {
case "INCREMENT": return state + 1;
case "DECREMENT": return state - 1;
default: return state;
}
};
}
const Counter = () => (
<Container initialState={initialState} onMount={onMount} onUpdate={onUpdate}>
{({ count }) => <button>{count}</button>}
</Container>
);
```
function useCounter(context) {
const [count, dispatch] = useContextReducer(context, reducer, 0);
const increment = () => dispatch({ type: "INCREMENT" });
const decrement = () => dispatch({ type: "DECREMENT" });
return { count, increment, decrement };
}
<p align="center"><img src="https://user-images.githubusercontent.com/3068563/40272385-512d74de-5b7a-11e8-85da-974e53c7887f.gif" alt="Example"></p>
### `onUnmount`
```ts
type OnUnmount = (props: {
state: object,
setState: Function
}) => void;
function ContextualCounter() {
const { count, increment } = useCounter("counter1");
return <button onClick={increment}>{count}</button>;
}
```
This is a function called inside `Container`'s `componentWillUnmount`. It receives both current `state` and `setState`, but the latter will have effect only if you're using [`context`](#context). Otherwise, it will be noop. This is useful for making cleanups.
<br>
> Note: when using [`context`](#context), all `Container`s of the same context behave as a single unit, which means that `onUnmount` will be called only when the last remaining `Container` of each context gets unmounted.
## `useContextEffect`
```jsx
const initialState = { count: 0 };
<sup><a href="#table-of-contents">↑ Back to top</a></sup>
const onMount = ({ setState }) => {
const fn = () => setState(state => ({ count: state.count + 1 }));
const interval = setInterval(fn, 1000);
setState({ interval });
};
Constate provides all contextual versions of [`React.useEffect`](https://reactjs.org/docs/hooks-reference.html#useeffect), such as `useContextEffect`, `useContextMutationEffect` and `useContextLayoutEffect`.
const onUnmount = ({ state }) => {
clearInterval(state.interval);
};
They receive `contextKey` as the first argument. If `contextKey` is `null` or `undefined`, the hook will work exactly as the React one.
const Counter = () => (
<Container initialState={initialState} onMount={onMount} onUnmount={onUnmount}>
{({ count }) => <button>{count}</button>}
</Container>
);
```
<p align="center"><img src="https://user-images.githubusercontent.com/3068563/40272496-083be31c-5b7c-11e8-9e0e-5fa623495ed4.gif" alt="Example"></p>
### `shouldUpdate`
```ts
type ShouldUpdate = (props: {
state: object,
nextState: object
}) => boolean;
```
This is a function called inside `Container`s `shouldComponentUpdate`. It receives the current `state` and `nextState` and should return `true` or `false`. If it returns `false`, `onUpdate` won't be called for that change, and it won't trigger another render.
In the previous example using [`onUnmount`](#onunmount), we stored the result of `setInterval` in the state. That's ok to do, but the downside is that it would trigger an additional render, even though our UI didn't depend on `state.interval`. We can use `shouldUpdate` to ignore `state.interval`, for example:
```jsx
const initialState = { count: 0, updates: 0 };
const onMount = ({ setState }) => {
const fn = () => setState(state => ({ count: state.count + 1 }));
const interval = setInterval(fn, 1000);
setState({ interval });
};
const onUnmount = ({ state }) => {
clearInterval(state.interval);
};
const onUpdate = ({ type, setState }) => {
// prevent infinite loop
if (type !== "onUpdate") {
setState(state => ({ updates: state.updates + 1 }));
}
};
// Don't call onUpdate and render if `interval` has changed
const shouldUpdate = ({ state, nextState }) =>
state.interval === nextState.interval;
const Counter = () => (
<Container
initialState={initialState}
onMount={onMount}
onUnmount={onUnmount}
onUpdate={onUpdate}
shouldUpdate={shouldUpdate}
>
{({ count, updates }) => (
<Button>
Count: {count}
<br />
Updates: {updates}
</Button>
)}
</Container>
);
```
<p align="center"><img src="https://user-images.githubusercontent.com/3068563/42988517-3d1f069a-8bd3-11e8-9742-e3294a863bb0.gif" alt="Example"></p>
### `pure`
```ts
type Pure = boolean;
```
You can pass `pure` prop to your `Container` so it will re-render only if its `state` has changed, ignoring other props. It'll help you if you're having performance issues within your app.
Due to how React Context works, all `Container`s with a [`context`](#context) prop will re-render when context state changes. Using `pure` will ensure that it will trigger an update only for its own context.
`pure` is equivalent to this implementation of [`shouldUpdate`](#shouldupdate):
```js
const shouldUpdate = ({ state, nextState }) => state !== nextState;
```
import { Provider, useContextEffect } from "constate";
But you can use both props. `shouldUpdate` will be applied after `pure`, which means that, if state is the same, it'll never re-render even if you do `shouldUpdate={() => true}`.
let count = 0;
> It's safe to pass `pure` to all your containers, and remove it only if you see unintended results on your UI, such as some part of the UI not updating correctly.
## `Provider`
You should wrap your app with `Provider` if you want to use [`context`](#context).
### `initialState`
```ts
type InitialState = object;
```
It's possible to pass initialState to Provider. In the example below, all `Container`s with `context="counter1"` will start with `{ count: 10 }`.
> Note: when using [`context`](#context), only the `initialState` of the first `Container` in the tree will be considered. `Provider` will always take precedence over `Container`.
```jsx
const initialState = {
counter1: {
count: 10
}
};
const App = () => (
<Provider initialState={initialState}>
...
</Provider>
);
```
### `onMount`
```ts
type OnMount = (props: {
state: object,
setContextState: Function
}) => void;
```
As well as with `Container`, you can pass an `onMount` prop to `Provider`. The function will be called when `Provider`'s `componentDidMount` gets called.
```jsx
const onMount = ({ setContextState }) => {
setContextState("counter1", { count: 0 });
};
const MyProvider = props => (
<Provider onMount={onMount} {...props} />
);
const App = () => (
<MyProvider>
...
</MyProvider>
);
```
### `onUpdate`
```ts
type OnUpdate = (props: {
prevState: object,
state: object,
setContextState: Function,
context: string,
type: string
}) => void;
```
`onUpdate` will be called every time `Provider`'s `setState` gets called. If `setContextState` was called instead, `onUpdate` will also receive a `context` prop.
`Container`s, when the `context` prop is defined, use `setContextState` internally, which means that `Provider`'s `onUpdate` will be triggered for every change on the context.
```jsx
const initialState = { counter1: { incrementCalls: 0 } };
const onUpdate = ({ context, type, setContextState }) => {
if (type === "increment") {
setContextState(context, state => ({
incrementCalls: state.incrementCalls + 1
}));
}
};
const MyProvider = props => (
<Provider initialState={initialState} onUpdate={onUpdate} {...props} />
);
const CounterContainer = props => (
<Container
initialState={{ count: 0 }}
actions={{ increment: () => state => ({ count: state.count + 2 }) }}
{...props}
/>
);
const Counter = () => (
<MyProvider>
<CounterContainer context="counter1">
{({ count, incrementCalls, increment }) => (
<button onClick={increment}>
count: {count}<br />
incrementCalls: {incrementCalls}
</button>
)}
</CounterContainer>
</MyProvider>
);
```
<p align="center"><img src="https://user-images.githubusercontent.com/3068563/40588505-8e10891e-61b4-11e8-81ae-c947220ae4d2.gif" alt="Example"></p>
### `onUnmount`
```ts
type OnUnmount = (props: { state: object }) => void;
```
`onUnmount` will be triggered in `Provider`'s `componentWillUnmount`.
```jsx
const onUnmount = ({ state }) => {
console.log(state);
};
const App = () => (
<Provider onUnmount={onUnmount}>
...
</Provider>
);
```
### `devtools`
```ts
type Devtools = boolean;
```
Passing `devtools` prop to `Provider` will enable [redux-devtools-extension](https://github.com/zalmoxisus/redux-devtools-extension) integration, if that's installed on your browser. With that, you can easily debug the state of your application.
```jsx
const App = () => (
<Provider devtools>
...
</Provider>
);
```
<p align="center"><img src="https://user-images.githubusercontent.com/3068563/40630279-c31d4f36-62a7-11e8-96f2-052c97985747.gif" alt="Example"></p>
Note that it only works for context state. If you want to debug local state, you can add a `context` prop to `Container` temporarily.
Alternatively, you can use `onUpdate` to log changes on the console:
```jsx
<CounterContainer onUpdate={console.log} />
```
## TypeScript
Constate is written in TypeScript and exports many useful types to help you. When creating a new container, you can start by defining its **public API**. That is, the props that are passed to the children function:
```ts
interface State {
count: number;
function useCounter(context) {
useContextEffect(context, () => {
count += 1;
}, []);
}
interface Actions {
increment: (amount?: number) => void; // actions always return void
function ContextualCounter1() {
useCounter("counter1");
...
}
interface Selectors {
getParity: () => "even" | "odd";
function ContextualCounter2() {
useCounter("counter1");
...
}
interface Effects {
tick: () => void;
function App() {
return (
<Provider>
<ContextualCounter1 />
<ContextualCounter2 />
</Provider>
);
}
```
In computer programming, it's a good practice to define the API before actually implementing it. This way, you're explicitly saying how the container should be consumed. Then, you can use handful interfaces exported by Constate to create your container:
In the example above, if we were using [`React.useEffect`](https://reactjs.org/docs/hooks-reference.html#useeffect), `count` would be `2`. With `useContextEffect`, it's `1`.
```ts
import { ActionMap, SelectorMap, EffectMap } from "constate";
`useContextEffect` ensures that the function will be called only once per `contextKey` no matter how many components are using it.
const initialState: State = {
count: 0
};
<br>
const actions: ActionMap<State, Actions> = {
increment: (amount = 1) => state => ({ count: state.count + amount })
};
## `createContext`
const selectors: SelectorMap<State, Selectors> = {
getParity: () => state => (state.count % 2 === 0 ? "even" : "odd")
};
<sup><a href="#table-of-contents">↑ Back to top</a></sup>
const effects: EffectsMap<State, Effects> = {
tick: () => ({ setState }) => {
const fn = () => setState(state => ({ count: state.count + 1 }));
setInterval(fn, 1000);
}
}
```
If you want to set a initial state for the whole context tree and/or want to create separate contexts, you can use `createContext`:
Those interfaces (e.g. `ActionMap`) will create a map using your `State` and your public API (e.g. `Actions`).
```js
// MyContext.js
import { createContext } from "constate";
If you're using VSCode or other code editor that supports TypeScript, you'll probably have a great developer experience. Tooling will infer types and give you autocomplete for most things:
const {
Provider,
useContextState,
useContextReducer,
useContextEffect,
useContextMutationEffect,
useContextLayoutEffect
} = createContext({
counter1: 0,
posts: [
{ id: 1, title: "Hello World!" }
]
});
<p align="center"><img src="https://user-images.githubusercontent.com/3068563/44436323-eb817580-a58a-11e8-8077-9f27ff283c5e.gif" alt="Example"></p>
Then, you just need to pass those maps to [`Container`](#container):
```jsx
const Counter = () => (
<Container
initialState={initialState}
actions={actions}
selectors={selectors}
effects={effects}
>
{({ count, increment, getParity, tick }) => ...}
</Container>
);
export {
Provider,
useContextState,
useContextReducer,
useContextEffect,
useContextMutationEffect,
useContextLayoutEffect
};
```
It'll also provide autocomplete and hints on the public API:
<p align="center"><img src="https://user-images.githubusercontent.com/3068563/44436560-3fd92500-a58c-11e8-84b2-cd9c8c3fddc6.gif" alt="Example"></p>
If you're building a composable container - that is, a component without `children` that receives props -, you can define your component as a `ComposableContainer`:
```jsx
import { Container, ComposableContainer } from "constate";
// App.js
import React from "react";
import { Provider, useContextState } from "./MyContext";
const CounterContainer: ComposableContainer<State, Actions, Selectors, Effects> = props => (
<Container
{...props}
initialState={{ ...initialState, ...props.initialState }}
actions={actions}
selectors={selectors}
effects={effects}
/>
);
```
function Counter() {
// no need for initial value, it has been set in context
const [count, setCount] = useContextState("counter1");
const increment = () => setCount(count + 1);
return <button onClick={increment}>{count}</button>;
}
Then, you can use it in other parts of your application and still take advantage from typings. `ComposableContainer` will handle them for you:
```jsx
const Counter = () => (
<CounterContainer initialState={{ count: 10 }} context="counter1">
{({ count, increment, getParity, tick }) => ...}
</CounterContainer>
);
function App() {
return (
<Provider>
<Counter />
</Provider>
);
}
```
> `ComposableContainer` doesn't accept other `actions`, `selectors` and `effects` as props. That's because, as of today, there's no way for TypeScript to dynamically merge props and infer their types correctly.
> When importing hooks directly from the `constate` package, you're, in fact, using a default context created by our [index file](src/index.ts).
There're also useful interfaces for lifecycle methods. You can find them all in [`src/types.ts`](src/types.ts).
`createContext` receives a second argument `name`, which will be displayed in the Redux Devtools when using the `devtools` prop on `Provider`.
## Testing
<br>
[`actions`](#actions) and [`selectors`](#selectors) are pure functions and you can test them directly:
```js
test("increment", () => {
expect(increment(1)({ count: 0 })).toEqual({ count: 1 });
expect(increment(-1)({ count: 1 })).toEqual({ count: 0 });
});
test("getParity", () => {
expect(getParity()({ count: 0 })).toBe("even");
expect(getParity()({ count: 1 })).toBe("odd");
});
```
On the other hand, [`effects`](#effects) and lifecycle methods can be a little tricky to test depending on how you implement them.
## Contributing

@@ -709,6 +311,8 @@

Run `npm start` to run examples.
When working on this codebase, please use `yarn`. Run `yarn examples:start` to run examples.
<br>
## License
MIT © [Diego Haz](https://github.com/diegohaz)

@@ -1,7 +0,22 @@

import Consumer from "./Consumer";
import Container from "./Container";
import Provider from "./Provider";
import createContext from "./createContext";
export * from "./types";
const {
Context,
Provider,
useContextReducer,
useContextState,
useContextEffect,
useContextLayoutEffect,
useContextMutationEffect
} = createContext<{ [key: string]: any }>({});
export { Consumer, Container, Provider };
export {
createContext,
Context,
Provider,
useContextReducer,
useContextState,
useContextEffect,
useContextLayoutEffect,
useContextMutationEffect
};

@@ -1,294 +0,9 @@

import * as React from "react";
export type StateUpdater<State> = (state: State) => State;
export type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
export type SetState<State> = (state: State | StateUpdater<State>) => void;
export type EventKeys = "onMount" | "onUpdate" | "onUnmount" | "initialState";
export type Reducer<State, Action> = (state: State, action: Action) => State;
export type StateUpdater<S> = (state: Readonly<S>) => Partial<S>;
export type ContextState<State> = [State, SetState<State>];
export type StateCallback = () => void;
export type SetState<S> = (
updaterOrState: StateUpdater<S> | Partial<S>,
callback?: () => void
) => void;
/**
* A React `setState` alternative implementation that receives a `type`
* argument
* @template S State
* @template K Possible values of the `type` argument
*/
export type SetStateWithType<S, K> = (
updaterOrState: StateUpdater<S> | Partial<S>,
callback: StateCallback | undefined,
type: K
) => void;
/**
* A function passed by `Provider` to `ContextContainer` to be used as a
* contextual `setState`
* @template S State
* @template K Possible values of the `type` argument
*/
export type SetContextState<S, K> = (
context: string,
updaterOrState: StateUpdater<S> | Partial<S>,
callback?: StateCallback,
type?: K
) => void;
/**
* Action implementation based on public action T
* @template S State
* @template T Action to be passed to the children function
*/
export type Action<S, T> = T extends (...args: infer U) => any
? (...args: U) => StateUpdater<S>
: (...args: unknown[]) => StateUpdater<S>;
/**
* Selector implementation based on public selector T
* @template S State
* @template T Selector to be passed to the children function
*/
export type Selector<S, T> = T extends (...args: infer U) => infer R
? (...args: U) => (state: Readonly<S>) => R
: (...args: unknown[]) => (state: Readonly<S>) => any;
/**
* Props received by the effect function implementation
* @template S State
*/
export interface EffectProps<S> {
state: Readonly<S>;
setState: SetState<S>;
}
/**
* Effect implementation based on public effect T
* @template S State
* @template T Effect to be passed to the children function
*/
export type Effect<S, T> = T extends (...args: infer U) => infer R
? (...args: U) => (props: EffectProps<S>) => R
: (...args: unknown[]) => (props: EffectProps<S>) => any;
/**
* Map of container action implementations
* @template S State
* @template P Map of actions to be passed to the children function
*/
export type ActionMap<S, P> = { [K in keyof P]: Action<S, P[K]> };
/**
* Map of container selector implementations
* @template S State
* @template P Map of selectors to be passed to the children function
*/
export type SelectorMap<S, P> = { [K in keyof P]: Selector<S, P[K]> };
/**
* Map of container effect implementations
* @template S State
* @template P Map of effects to be passed to the children function
*/
export type EffectMap<S, P> = { [K in keyof P]: Effect<S, P[K]> };
/**
* Props received by the Container `onMount` function prop
* @template S State
*/
export interface OnMountProps<S> extends EffectProps<S> {}
/**
* Container `onMount` prop
* @template S State
*/
export interface OnMount<S> {
(props: OnMountProps<S>): any;
}
/**
* Props received by the Container `onUpdate` function prop
* @template S State
* @template AP Actions
* @template EP Effects
*/
export interface OnUpdateProps<S, AP = {}, EP = {}> extends EffectProps<S> {
prevState: Readonly<S>;
type: keyof AP | keyof EP | EventKeys;
}
/**
* Container `onUpdate` prop
* @template S State
* @template AP Actions
* @template EP Effects
*/
export interface OnUpdate<S, AP = {}, EP = {}> {
(props: OnUpdateProps<S, AP, EP>): any;
}
/**
* Props received by the Container `onUnmount` function prop
* @template S State
*/
export interface OnUnmountProps<S> extends EffectProps<S> {}
/**
* Container `onUnmount` prop
* @template S State
*/
export interface OnUnmount<S> {
(props: OnUnmountProps<S>): any;
}
/**
* Props received by the Container `shouldUpdate` function prop
* @template S State
*/
export interface ShouldUpdateProps<S> {
state: Readonly<S>;
nextState: Readonly<S>;
}
/**
* Container `shouldUpdate` prop
* @template S State
*/
export interface ShouldUpdate<S> {
(props: ShouldUpdateProps<S>): boolean;
}
/**
* Props received by the Provider `onMount` function prop
* @template S State
*/
export interface ProviderOnMountProps<S> {
state: S;
setContextState: SetContextState<S[keyof S], string>;
}
/**
* Provider `onMount` prop
* @template S State
*/
export interface ProviderOnMount<S> {
(props: ProviderOnMountProps<S>): any;
}
/**
* Props received by the Provider `onUpdate` function prop
* @template S State
*/
export interface ProviderOnUpdateProps<S> {
prevState: S;
state: S;
setContextState: SetContextState<S[keyof S], string>;
context: string;
type?: string;
}
/**
* Provider `onUpdate` prop
* @template S State
*/
export interface ProviderOnUpdate<S> {
(props: ProviderOnUpdateProps<S>): any;
}
/**
* Props received by the Provider `onUnmount` function prop
* @template S State
*/
export interface ProviderOnUnmountProps<S> {
state: S;
}
/**
* Provider `onUnmount` prop
* @template S State
*/
export interface ProviderOnUnmount<S> {
(props: ProviderOnUnmountProps<S>): any;
}
/**
* Mounts a container and returns a function to unmount it.
*/
export type MountContainer = (
context: string,
onMount?: () => void
) => (onUnmount?: () => void) => void;
/**
* `Container` props
* @template S State
* @template AP Map of actions to be passed to the children function
* @template SP Map of selectors to be passed to the children function
* @template EP Map of effects to be passed to the children function
*/
export interface ContainerProps<S, AP = {}, SP = {}, EP = {}> {
initialState?: Partial<S>;
context?: string;
actions?: ActionMap<S, AP>;
selectors?: SelectorMap<S, SP>;
effects?: EffectMap<S, EP>;
onMount?: OnMount<S>;
onUpdate?: OnUpdate<S, AP, EP>;
onUnmount?: OnUnmount<S>;
shouldUpdate?: ShouldUpdate<S>;
pure?: boolean;
children: (props: S & AP & SP & EP) => React.ReactNode;
}
/**
* `InnerContainer` props
* @template S State
* @template AP Map of actions to be passed to the children function
* @template SP Map of selectors to be passed to the children function
* @template EP Map of effects to be passed to the children function
*/
export interface InnerContainerProps<S, AP = {}, SP = {}, EP = {}>
extends ContainerProps<S, AP, SP, EP> {
state?: S;
setContextState?: SetContextState<S, keyof AP | keyof EP | EventKeys>;
mountContainer?: MountContainer;
}
/**
* Props for composable container components
* @template S State
* @template AP Map of actions to be passed to the children function
* @template SP Map of selectors to be passed to the children function
* @template EP Map of effects to be passed to the children function
*/
export type ComposableContainerProps<S, AP = {}, SP = {}, EP = {}> = Omit<
ContainerProps<S, AP, SP, EP>,
"actions" | "selectors" | "effects"
>;
/**
* A composable container is a component that renders `Container` without
* `children` and receives props
* @template S State
* @template AP Map of actions to be passed to the children function
* @template SP Map of selectors to be passed to the children function
* @template EP Map of effects to be passed to the children function
*/
export interface ComposableContainer<S, AP = {}, SP = {}, EP = {}> {
(props: ComposableContainerProps<S, AP, SP, EP>): JSX.Element;
}
/**
* `Provider` props
* @template S State
*/
export interface ProviderProps<S> {
initialState?: Partial<S>;
devtools?: boolean;
onMount?: ProviderOnMount<S>;
onUpdate?: ProviderOnUpdate<S>;
onUnmount?: ProviderOnUnmount<S>;
}
export type ContextReducer<State, Action> = [State, (action: Action) => void];

@@ -1,60 +0,17 @@

import {
ActionMap,
SelectorMap,
EffectMap,
StateUpdater,
EffectProps,
SetStateWithType
} from "./types";
import { StateUpdater } from "./types";
const keys = Object.keys as <T>(o: T) => (keyof T)[];
const hashMap: { [key: string]: number } = {};
export function mapSetStateToActions<S, P>(
setState: SetStateWithType<S, keyof P>,
actionMap: ActionMap<S, P>
) {
return keys(actionMap).reduce(
(map, type) =>
Object.assign(map, {
[type]: (...args: any[]) =>
setState(actionMap[type](...args), undefined, type)
}),
{} as P
);
export function hash(string: string) {
if (hashMap[string] !== undefined) {
return hashMap[string];
}
const { length } = Object.keys(hashMap);
hashMap[string] = 1 << length;
return hashMap[string];
}
export function mapStateToSelectors<S, P>(
state: S,
selectorMap: SelectorMap<S, P>
) {
return keys(selectorMap).reduce(
(map, type) =>
Object.assign(map, {
[type]: (...args: any[]) => selectorMap[type](...args)(state)
}),
{} as P
);
export function parseUpdater<S>(state: StateUpdater<S> | S, prevState: S) {
const isUpdater = (a: any): a is StateUpdater<S> => typeof a === "function";
return isUpdater(state) ? state(prevState) : state;
}
export function mapPropsToEffects<S, P>(
getEffectProps: (type: keyof P) => EffectProps<S>,
effectMap: EffectMap<S, P>
) {
return keys(effectMap).reduce(
(final, type) =>
Object.assign(final, {
[type]: (...args: any[]) =>
effectMap[type](...args)(getEffectProps(type))
}),
{} as P
);
}
export function parseUpdater<S>(
updaterOrState: StateUpdater<S> | Partial<S>,
state: S
) {
return typeof updaterOrState === "function"
? updaterOrState(state)
: updaterOrState;
}
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc