constate
Advanced tools
Comparing version 1.0.0-alpha.3 to 1.0.0-alpha.4
@@ -5,2 +5,17 @@ # Change Log | ||
<a name="1.0.0-alpha.4"></a> | ||
# [1.0.0-alpha.4](https://github.com/diegohaz/constate/compare/v1.0.0-alpha.3...v1.0.0-alpha.4) (2018-11-27) | ||
### Features | ||
* Add new `useContextKey` hook ([e0d8cd8](https://github.com/diegohaz/constate/commit/e0d8cd8)) | ||
### BREAKING CHANGES | ||
* Removed `unstable_` prefix from `useContextEffect` hooks. They're no longer using React internal stuff, but now they require `useContextKey`. See [docs](https://github.com/diegohaz/constate#usecontexteffect). | ||
<a name="1.0.0-alpha.3"></a> | ||
@@ -7,0 +22,0 @@ # [1.0.0-alpha.3](https://github.com/diegohaz/constate/compare/v1.0.0-alpha.2...v1.0.0-alpha.3) (2018-11-25) |
@@ -7,6 +7,2 @@ 'use strict'; | ||
function getCurrentOwner() { | ||
return React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner.current; | ||
} | ||
function createUseContextEffect(type) { | ||
@@ -19,17 +15,17 @@ if (type === void 0) { | ||
return function useContextEffect(contextKey, create, inputs) { | ||
var key = contextKey; | ||
var consumer = React.useRef(getCurrentOwner()); | ||
var key = contextKey ? contextKey.current : contextKey; | ||
if (consumers[key] == null) { | ||
consumers[key] = consumer.current; | ||
if (key && consumers[key] == null) { | ||
consumers[key] = contextKey; | ||
} | ||
React.useMutationEffect(function () { | ||
if (!key) return undefined; | ||
return function () { | ||
consumers[key] = null; | ||
if (key && consumers[key] === contextKey) { | ||
consumers[key] = null; | ||
} | ||
}; | ||
}, [key]); | ||
React[type](function () { | ||
if (!key || consumers[key] === consumer.current) { | ||
if (!key || consumers[key] === contextKey) { | ||
return create(); | ||
@@ -47,4 +43,5 @@ } | ||
return function useContextReducer(contextKey, reducer, initialState, initialAction) { | ||
// @ts-ignore | ||
var _React$useContext = React.useContext(contextKey ? context : EmptyContext, contextKey ? hash(contextKey) : undefined), | ||
var key = typeof contextKey === "object" && contextKey ? contextKey.current : contextKey; // @ts-ignore | ||
var _React$useContext = React.useContext(key ? context : EmptyContext, key ? hash(key) : undefined), | ||
contextState = _React$useContext[0], | ||
@@ -57,5 +54,5 @@ setContextState = _React$useContext[1]; | ||
if (contextKey) { | ||
if (contextState[contextKey] != null) { | ||
state = contextState[contextKey]; | ||
if (key) { | ||
if (contextState[key] != null) { | ||
state = contextState[key]; | ||
} | ||
@@ -67,3 +64,3 @@ | ||
return Object.assign({}, prevState, (_Object$assign = {}, _Object$assign[contextKey] = reducer(prevState[contextKey], action), _Object$assign)); | ||
return Object.assign({}, prevState, (_Object$assign = {}, _Object$assign[key] = reducer(prevState[key], action), _Object$assign)); | ||
}); | ||
@@ -74,8 +71,8 @@ }; | ||
React.useMutationEffect(function () { | ||
if (contextKey && contextState[contextKey] == null && state != null) { | ||
if (key && contextState[key] == null && state != null) { | ||
setContextState(function (prevState) { | ||
if (prevState[contextKey] == null) { | ||
if (prevState[key] == null) { | ||
var _Object$assign2; | ||
return Object.assign({}, prevState, (_Object$assign2 = {}, _Object$assign2[contextKey] = state, _Object$assign2)); | ||
return Object.assign({}, prevState, (_Object$assign2 = {}, _Object$assign2[key] = state, _Object$assign2)); | ||
} | ||
@@ -86,3 +83,3 @@ | ||
} | ||
}, [contextKey]); | ||
}, [key]); | ||
return [state, dispatch]; | ||
@@ -107,4 +104,3 @@ }; | ||
var _ref = _temp === void 0 ? {} : _temp, | ||
_ref$enabled = _ref.enabled, | ||
enabled = _ref$enabled === void 0 ? true : _ref$enabled; | ||
enabled = _ref.enabled; | ||
@@ -155,2 +151,9 @@ var devtools = React.useRef(null); | ||
function createUseContextKey() { | ||
return function useContextKey(initialContextKey) { | ||
var key = React.useRef(initialContextKey); | ||
return initialContextKey ? key : undefined; | ||
}; | ||
} | ||
function createHash(skipLength) { | ||
@@ -218,7 +221,8 @@ var hashMap = {}; | ||
Provider: createProvider(Context, initialState), | ||
useContextKey: createUseContextKey(), | ||
useContextState: createUseContextState(Context, hash), | ||
useContextReducer: createUseContextReducer(Context, hash), | ||
unstable_useContextEffect: createUseContextEffect(), | ||
unstable_useContextLayoutEffect: createUseContextEffect("useLayoutEffect"), | ||
unstable_useContextMutationEffect: createUseContextEffect("useMutationEffect") | ||
useContextEffect: createUseContextEffect(), | ||
useContextLayoutEffect: createUseContextEffect("useLayoutEffect"), | ||
useContextMutationEffect: createUseContextEffect("useMutationEffect") | ||
}; | ||
@@ -232,5 +236,6 @@ } | ||
useContextState = _createContext.useContextState, | ||
unstable_useContextEffect = _createContext.unstable_useContextEffect, | ||
unstable_useContextLayoutEffect = _createContext.unstable_useContextLayoutEffect, | ||
unstable_useContextMutationEffect = _createContext.unstable_useContextMutationEffect; | ||
useContextKey = _createContext.useContextKey, | ||
useContextEffect = _createContext.useContextEffect, | ||
useContextLayoutEffect = _createContext.useContextLayoutEffect, | ||
useContextMutationEffect = _createContext.useContextMutationEffect; | ||
@@ -242,4 +247,5 @@ exports.createContext = createContext; | ||
exports.useContextState = useContextState; | ||
exports.unstable_useContextEffect = unstable_useContextEffect; | ||
exports.unstable_useContextLayoutEffect = unstable_useContextLayoutEffect; | ||
exports.unstable_useContextMutationEffect = unstable_useContextMutationEffect; | ||
exports.useContextKey = useContextKey; | ||
exports.useContextEffect = useContextEffect; | ||
exports.useContextLayoutEffect = useContextLayoutEffect; | ||
exports.useContextMutationEffect = useContextMutationEffect; |
import * as React from 'react'; | ||
import { useState, useMemo, createElement, createContext, __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED, useRef, useMutationEffect, useContext, useReducer, useEffect } from 'react'; | ||
import { useState, useMemo, createElement, createContext, useMutationEffect, useContext, useReducer, useRef, useEffect } from 'react'; | ||
function getCurrentOwner() { | ||
return __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner.current; | ||
} | ||
function createUseContextEffect(type) { | ||
@@ -15,17 +11,17 @@ if (type === void 0) { | ||
return function useContextEffect(contextKey, create, inputs) { | ||
var key = contextKey; | ||
var consumer = useRef(getCurrentOwner()); | ||
var key = contextKey ? contextKey.current : contextKey; | ||
if (consumers[key] == null) { | ||
consumers[key] = consumer.current; | ||
if (key && consumers[key] == null) { | ||
consumers[key] = contextKey; | ||
} | ||
useMutationEffect(function () { | ||
if (!key) return undefined; | ||
return function () { | ||
consumers[key] = null; | ||
if (key && consumers[key] === contextKey) { | ||
consumers[key] = null; | ||
} | ||
}; | ||
}, [key]); | ||
React[type](function () { | ||
if (!key || consumers[key] === consumer.current) { | ||
if (!key || consumers[key] === contextKey) { | ||
return create(); | ||
@@ -43,4 +39,5 @@ } | ||
return function useContextReducer(contextKey, reducer, initialState, initialAction) { | ||
// @ts-ignore | ||
var _React$useContext = useContext(contextKey ? context : EmptyContext, contextKey ? hash(contextKey) : undefined), | ||
var key = typeof contextKey === "object" && contextKey ? contextKey.current : contextKey; // @ts-ignore | ||
var _React$useContext = useContext(key ? context : EmptyContext, key ? hash(key) : undefined), | ||
contextState = _React$useContext[0], | ||
@@ -53,5 +50,5 @@ setContextState = _React$useContext[1]; | ||
if (contextKey) { | ||
if (contextState[contextKey] != null) { | ||
state = contextState[contextKey]; | ||
if (key) { | ||
if (contextState[key] != null) { | ||
state = contextState[key]; | ||
} | ||
@@ -63,3 +60,3 @@ | ||
return Object.assign({}, prevState, (_Object$assign = {}, _Object$assign[contextKey] = reducer(prevState[contextKey], action), _Object$assign)); | ||
return Object.assign({}, prevState, (_Object$assign = {}, _Object$assign[key] = reducer(prevState[key], action), _Object$assign)); | ||
}); | ||
@@ -70,8 +67,8 @@ }; | ||
useMutationEffect(function () { | ||
if (contextKey && contextState[contextKey] == null && state != null) { | ||
if (key && contextState[key] == null && state != null) { | ||
setContextState(function (prevState) { | ||
if (prevState[contextKey] == null) { | ||
if (prevState[key] == null) { | ||
var _Object$assign2; | ||
return Object.assign({}, prevState, (_Object$assign2 = {}, _Object$assign2[contextKey] = state, _Object$assign2)); | ||
return Object.assign({}, prevState, (_Object$assign2 = {}, _Object$assign2[key] = state, _Object$assign2)); | ||
} | ||
@@ -82,3 +79,3 @@ | ||
} | ||
}, [contextKey]); | ||
}, [key]); | ||
return [state, dispatch]; | ||
@@ -103,4 +100,3 @@ }; | ||
var _ref = _temp === void 0 ? {} : _temp, | ||
_ref$enabled = _ref.enabled, | ||
enabled = _ref$enabled === void 0 ? true : _ref$enabled; | ||
enabled = _ref.enabled; | ||
@@ -151,2 +147,9 @@ var devtools = useRef(null); | ||
function createUseContextKey() { | ||
return function useContextKey(initialContextKey) { | ||
var key = useRef(initialContextKey); | ||
return initialContextKey ? key : undefined; | ||
}; | ||
} | ||
function createHash(skipLength) { | ||
@@ -214,7 +217,8 @@ var hashMap = {}; | ||
Provider: createProvider(Context, initialState), | ||
useContextKey: createUseContextKey(), | ||
useContextState: createUseContextState(Context, hash), | ||
useContextReducer: createUseContextReducer(Context, hash), | ||
unstable_useContextEffect: createUseContextEffect(), | ||
unstable_useContextLayoutEffect: createUseContextEffect("useLayoutEffect"), | ||
unstable_useContextMutationEffect: createUseContextEffect("useMutationEffect") | ||
useContextEffect: createUseContextEffect(), | ||
useContextLayoutEffect: createUseContextEffect("useLayoutEffect"), | ||
useContextMutationEffect: createUseContextEffect("useMutationEffect") | ||
}; | ||
@@ -228,6 +232,7 @@ } | ||
useContextState = _createContext.useContextState, | ||
unstable_useContextEffect = _createContext.unstable_useContextEffect, | ||
unstable_useContextLayoutEffect = _createContext.unstable_useContextLayoutEffect, | ||
unstable_useContextMutationEffect = _createContext.unstable_useContextMutationEffect; | ||
useContextKey = _createContext.useContextKey, | ||
useContextEffect = _createContext.useContextEffect, | ||
useContextLayoutEffect = _createContext.useContextLayoutEffect, | ||
useContextMutationEffect = _createContext.useContextMutationEffect; | ||
export { createContext$1 as createContext, Context, Provider, useContextReducer, useContextState, unstable_useContextEffect, unstable_useContextLayoutEffect, unstable_useContextMutationEffect }; | ||
export { createContext$1 as createContext, Context, Provider, useContextReducer, useContextState, useContextKey, useContextEffect, useContextLayoutEffect, useContextMutationEffect }; |
@@ -1,1 +0,1 @@ | ||
!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,E){"use strict";function l(o){void 0===o&&(o="useEffect");var c={};return function(e,t,n){var u=e,r=E.useRef(E.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner.current);null==c[u]&&(c[u]=r.current),E.useMutationEffect(function(){if(u)return function(){c[u]=null}},[u]),E[o](function(){if(!u||c[u]===r.current)return t()},n?[u].concat(n):void 0)}}var d=E.createContext([]);function v(a,l){return function(u,r,e,t){var n=E.useContext(u?a:d,u?l(u):void 0),o=n[0],c=n[1],f=E.useReducer(r,e,t),i=f[0],s=f[1];return u&&(null!=o[u]&&(i=o[u]),s=function(n){return c(function(e){var t;return Object.assign({},e,((t={})[u]=r(e[u],n),t))})}),E.useMutationEffect(function(){u&&null==o[u]&&null!=i&&c(function(e){return null!=e[u]?e:Object.assign({},e,((t={})[u]=i,t));var t})},[u]),[i,s]}}function _(e,t){return"function"==typeof t?t(e):t}var x="undefined"!=typeof window&&window.__REDUX_DEVTOOLS_EXTENSION__;function C(v,_){return function(e){var n,t,u,r,o,c,f,i,s=e.children,a=e.devtools,l=E.useState(_),d=E.useMemo(function(){return l},[l[0]]);return n=l[0],t=l[1],r=(void 0===(u={enabled:a})?{}:u).enabled,o=void 0===r||r,c=E.useRef(null),f=E.useRef(null),i=E.useRef(null),E.useEffect(function(){if(o&&x)return c.current=x.connect(),c.current.init(n),c.current.subscribe(function(e){"DISPATCH"===e.type&&e.state&&(i.current=JSON.parse(e.state),t(i.current))}),function(){c.current&&(c.current.unsubscribe(),x.disconnect())}},[o]),E.useEffect(function(){if(o&&x){if(i.current!==n){var e;for(var t in n)f.current&&n[t]!==f.current[t]&&(e=t);e&&c.current&&c.current.send(e,n)}f.current=n}},[n,o]),E.createElement(v.Provider,{value:d},s)}}function t(e,t){var n,u,r,o,c,f,i=(n=1,u={},function(e){if(void 0!==u[e])return u[e];var t=Object.keys(u).length;return u[e]=1<<t%(30-n)+n,u[e]}),s=void 0===t?(r=i,function(e,t){var n=1;if("object"!=typeof t||Array.isArray(t)||null===t)return n;for(var u in t)e[u]!==t[u]&&(n|=r(u));return n}):t,a=E.createContext([e,function(){}],s?function(e,t){var n=e[0],u=t[0];return s(n,u)}:void 0);return{Context:a,Provider:C(a,e),useContextState:(o=a,c=i,f=v(o,c),function(e,t){return f(e,_,t)}),useContextReducer:v(a,i),unstable_useContextEffect:l(),unstable_useContextLayoutEffect:l("useLayoutEffect"),unstable_useContextMutationEffect:l("useMutationEffect")}}var n=t({}),u=n.Context,r=n.Provider,o=n.useContextReducer,c=n.useContextState,f=n.unstable_useContextEffect,i=n.unstable_useContextLayoutEffect,s=n.unstable_useContextMutationEffect;e.createContext=t,e.Context=u,e.Provider=r,e.useContextReducer=o,e.useContextState=c,e.unstable_useContextEffect=f,e.unstable_useContextLayoutEffect=i,e.unstable_useContextMutationEffect=s,Object.defineProperty(e,"__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,x){"use strict";function d(r){void 0===r&&(r="useEffect");var o={};return function(e,t,n){var u=e?e.current:e;u&&null==o[u]&&(o[u]=e),x.useMutationEffect(function(){return function(){u&&o[u]===e&&(o[u]=null)}},[u]),x[r](function(){if(!u||o[u]===e)return t()},n?[u].concat(n):void 0)}}var l=x.createContext([]);function v(d,v){return function(e,u,t,n){var r="object"==typeof e&&e?e.current:e,o=x.useContext(r?d:l,r?v(r):void 0),c=o[0],f=o[1],i=x.useReducer(u,t,n),s=i[0],a=i[1];return r&&(null!=c[r]&&(s=c[r]),a=function(n){return f(function(e){var t;return Object.assign({},e,((t={})[r]=u(e[r],n),t))})}),x.useMutationEffect(function(){r&&null==c[r]&&null!=s&&f(function(e){return null!=e[r]?e:Object.assign({},e,((t={})[r]=s,t));var t})},[r]),[s,a]}}function C(e,t){return"function"==typeof t?t(e):t}var E="undefined"!=typeof window&&window.__REDUX_DEVTOOLS_EXTENSION__;function y(v,l){return function(e){var n,t,u,r,o,c,f,i=e.children,s=e.devtools,a=x.useState(l),d=x.useMemo(function(){return a},[a[0]]);return n=a[0],t=a[1],r=(void 0===(u={enabled:s})?{}:u).enabled,o=x.useRef(null),c=x.useRef(null),f=x.useRef(null),x.useEffect(function(){if(r&&E)return o.current=E.connect(),o.current.init(n),o.current.subscribe(function(e){"DISPATCH"===e.type&&e.state&&(f.current=JSON.parse(e.state),t(f.current))}),function(){o.current&&(o.current.unsubscribe(),E.disconnect())}},[r]),x.useEffect(function(){if(r&&E){if(f.current!==n){var e;for(var t in n)c.current&&n[t]!==c.current[t]&&(e=t);e&&o.current&&o.current.send(e,n)}c.current=n}},[n,r]),x.createElement(v.Provider,{value:d},i)}}function t(e,t){var n,u,r,o,c,f,i=(n=1,u={},function(e){if(void 0!==u[e])return u[e];var t=Object.keys(u).length;return u[e]=1<<t%(30-n)+n,u[e]}),s=void 0===t?(r=i,function(e,t){var n=1;if("object"!=typeof t||Array.isArray(t)||null===t)return n;for(var u in t)e[u]!==t[u]&&(n|=r(u));return n}):t,a=x.createContext([e,function(){}],s?function(e,t){var n=e[0],u=t[0];return s(n,u)}:void 0);return{Context:a,Provider:y(a,e),useContextKey:function(e){var t=x.useRef(e);return e?t:void 0},useContextState:(o=a,c=i,f=v(o,c),function(e,t){return f(e,C,t)}),useContextReducer:v(a,i),useContextEffect:d(),useContextLayoutEffect:d("useLayoutEffect"),useContextMutationEffect:d("useMutationEffect")}}var n=t({}),u=n.Context,r=n.Provider,o=n.useContextReducer,c=n.useContextState,f=n.useContextKey,i=n.useContextEffect,s=n.useContextLayoutEffect,a=n.useContextMutationEffect;e.createContext=t,e.Context=u,e.Provider=r,e.useContextReducer=o,e.useContextState=c,e.useContextKey=f,e.useContextEffect=i,e.useContextLayoutEffect=s,e.useContextMutationEffect=a,Object.defineProperty(e,"__esModule",{value:!0})}); |
@@ -9,8 +9,9 @@ import * as React from "react"; | ||
Provider: ({ children, devtools }: ProviderProps) => JSX.Element; | ||
useContextKey: import("./createUseContextKey").UseContextKey<State>; | ||
useContextState: import("./createUseContextState").UseContextState<State>; | ||
useContextReducer: import("./createUseContextReducer").UseContextReducer<State>; | ||
unstable_useContextEffect: (contextKey: keyof State | null | undefined, create: () => void | (() => void), inputs?: ReadonlyArray<any> | undefined) => void; | ||
unstable_useContextLayoutEffect: (contextKey: keyof State | null | undefined, create: () => void | (() => void), inputs?: ReadonlyArray<any> | undefined) => void; | ||
unstable_useContextMutationEffect: (contextKey: keyof State | null | undefined, create: () => void | (() => void), inputs?: ReadonlyArray<any> | undefined) => void; | ||
useContextEffect: (contextKey: React.MutableRefObject<keyof State> | null | undefined, create: () => void | (() => void), inputs?: ReadonlyArray<any> | undefined) => void; | ||
useContextLayoutEffect: (contextKey: React.MutableRefObject<keyof State> | null | undefined, create: () => void | (() => void), inputs?: ReadonlyArray<any> | undefined) => void; | ||
useContextMutationEffect: (contextKey: React.MutableRefObject<keyof State> | null | undefined, create: () => void | (() => void), inputs?: ReadonlyArray<any> | undefined) => void; | ||
}; | ||
export default createContext; |
@@ -1,2 +0,3 @@ | ||
declare function createUseContextEffect<State>(type?: "useEffect" | "useMutationEffect" | "useLayoutEffect"): (contextKey: keyof State | null | undefined, create: () => void | (() => void), inputs?: ReadonlyArray<any> | undefined) => void; | ||
import * as React from "react"; | ||
declare function createUseContextEffect<State>(type?: "useEffect" | "useMutationEffect" | "useLayoutEffect"): (contextKey: React.MutableRefObject<keyof State> | null | undefined, create: () => void | (() => void), inputs?: ReadonlyArray<any> | undefined) => void; | ||
export default createUseContextEffect; |
import * as React from "react"; | ||
import { ContextReducer, ContextState, Reducer } from "./types"; | ||
export interface UseContextReducer<State> { | ||
<K extends keyof State, Action>(contextKey: K | undefined | null, reducer: Reducer<State[K], Action>, initialState?: null, initialAction?: Action): ContextReducer<State[K], Action>; | ||
<K extends keyof State, S extends State[K], Action>(contextKey: K | undefined | null, reducer: Reducer<S, Action>, initialState?: S | (() => S) | null, initialAction?: Action): ContextReducer<S, Action>; | ||
<K extends keyof State, Action>(contextKey: React.MutableRefObject<K> | K | undefined | null, reducer: Reducer<State[K], Action>, initialState?: null, initialAction?: Action): ContextReducer<State[K], Action>; | ||
<K extends keyof State, S extends State[K], Action>(contextKey: React.MutableRefObject<K> | K | undefined | null, reducer: Reducer<S, Action>, initialState?: S | (() => S) | null, initialAction?: Action): ContextReducer<S, Action>; | ||
} | ||
declare function createUseContextReducer<State>(context: React.Context<ContextState<State>>, hash: (key: string) => number): UseContextReducer<State>; | ||
export default createUseContextReducer; |
import * as React from "react"; | ||
import { ContextState } from "./types"; | ||
export interface UseContextState<State> { | ||
<K extends keyof State>(contextKey?: K | null): ContextState<State[K]>; | ||
<K extends keyof State, S extends State[K]>(contextKey?: K | null, initialState?: S | (() => S) | null): ContextState<S>; | ||
<K extends keyof State>(contextKey?: React.MutableRefObject<K> | K | null): ContextState<State[K]>; | ||
<K extends keyof State, S extends State[K]>(contextKey?: React.MutableRefObject<K> | K | null, initialState?: S | (() => S) | null): ContextState<S>; | ||
} | ||
declare function createUseContextState<State>(context: React.Context<ContextState<State>>, hash: (key: string) => number): UseContextState<State>; | ||
export default createUseContextState; |
@@ -11,3 +11,5 @@ /// <reference types="react" /> | ||
[key: string]: any; | ||
}>, unstable_useContextEffect: (contextKey: string | number | null | undefined, create: () => void | (() => void), inputs?: ReadonlyArray<any> | undefined) => void, unstable_useContextLayoutEffect: (contextKey: string | number | null | undefined, create: () => void | (() => void), inputs?: ReadonlyArray<any> | undefined) => void, unstable_useContextMutationEffect: (contextKey: string | number | null | undefined, create: () => void | (() => void), inputs?: ReadonlyArray<any> | undefined) => void; | ||
export { createContext, Context, Provider, useContextReducer, useContextState, unstable_useContextEffect, unstable_useContextLayoutEffect, unstable_useContextMutationEffect }; | ||
}>, useContextKey: import("./createUseContextKey").UseContextKey<{ | ||
[key: string]: any; | ||
}>, useContextEffect: (contextKey: import("react").MutableRefObject<string | number> | null | undefined, create: () => void | (() => void), inputs?: ReadonlyArray<any> | undefined) => void, useContextLayoutEffect: (contextKey: import("react").MutableRefObject<string | number> | null | undefined, create: () => void | (() => void), inputs?: ReadonlyArray<any> | undefined) => void, useContextMutationEffect: (contextKey: import("react").MutableRefObject<string | number> | null | undefined, create: () => void | (() => void), inputs?: ReadonlyArray<any> | undefined) => void; | ||
export { createContext, Context, Provider, useContextReducer, useContextState, useContextKey, useContextEffect, useContextLayoutEffect, useContextMutationEffect }; |
{ | ||
"name": "constate", | ||
"version": "1.0.0-alpha.3", | ||
"version": "1.0.0-alpha.4", | ||
"description": "Yet another React state management library that lets you work with local state and scale up to global state with ease", | ||
@@ -5,0 +5,0 @@ "license": "MIT", |
168
README.md
@@ -6,24 +6,30 @@ <p align="center"> | ||
<p align="center"> | ||
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> | ||
# Constate | ||
<br> | ||
<a href="https://npmjs.org/package/constate"><img alt="NPM version" src="https://img.shields.io/npm/v/constate/next.svg?style=flat-square"></a> | ||
<a href="https://npmjs.org/package/constate"><img alt="NPM downloads" src="https://img.shields.io/npm/dm/constate.svg?style=flat-square"></a> | ||
<a href="https://unpkg.com/constate@next"><img alt="Gzip size" src="https://img.badgesize.io/https://unpkg.com/constate@next?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 align="center"> | ||
<a href="https://npmjs.org/package/constate"><img alt="NPM version" src="https://img.shields.io/npm/v/constate/next.svg?style=flat-square"></a> | ||
<a href="https://unpkg.com/constate@next"><img alt="Gzip size" src="https://img.badgesize.io/https://unpkg.com/constate@next?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> | ||
~1 kB React state management library that lets you write contextual state as if it were local state using [React Hooks](https://reactjs.org/docs/hooks-intro.html) and [React Context](https://reactjs.org/docs/context.html). | ||
<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> | ||
<table> | ||
<thead> | ||
<tr> | ||
<th colspan="3">🕹 CodeSandbox demos 🕹</th> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
<tr> | ||
<td><a href="https://codesandbox.io/s/github/diegohaz/constate/tree/master/examples/counter?module=/App.js">Counter</a></td> | ||
<td><a href="https://codesandbox.io/s/github/diegohaz/constate/tree/master/examples/theming?module=/App.js">Theming</a></td> | ||
<td><a href="https://codesandbox.io/s/github/diegohaz/constate/tree/master/examples/i18n?module=/App.js">I18n</a></td> | ||
</tr> | ||
</tbody> | ||
</table> | ||
@@ -36,16 +42,18 @@ <br> | ||
function useCounter(context) { | ||
// replacing React.useState(0); | ||
const [count, setCount] = useContextState(context, 0); | ||
// 1. Create a custom hook | ||
function useCounter(key) { | ||
// 2. Replace React.useState(0) by useContextState(key, 0) | ||
const [count, setCount] = useContextState(key, 0); | ||
const increment = () => setCount(count + 1); | ||
const decrement = () => setCount(count - 1); | ||
return { count, increment, decrement }; | ||
return { count, increment }; | ||
} | ||
function DecrementButton() { | ||
const { decrement } = useCounter("counter1"); | ||
return <button onClick={decrement}>-</button>; | ||
function Count() { | ||
// 3. Consume the custom hook as usual | ||
const { count } = useCounter("counter1"); | ||
return <span>{count}</span> | ||
} | ||
function IncrementButton() { | ||
// 4. Consume the same key in other components | ||
const { increment } = useCounter("counter1"); | ||
@@ -55,11 +63,6 @@ return <button onClick={increment}>+</button>; | ||
function Count() { | ||
const { count } = useCounter("counter1"); | ||
return <span>{count}</span> | ||
} | ||
function App() { | ||
// 5. Wrap your app with Provider | ||
return ( | ||
<Provider> | ||
<DecrementButton /> | ||
<Count /> | ||
@@ -80,2 +83,4 @@ <IncrementButton /> | ||
- [`useContextReducer`](#usecontextreducer) | ||
- [`useContextKey`](#usecontextkey) | ||
- [`useContextEffect`](#usecontexteffect) | ||
- [`createContext`](#createcontext) | ||
@@ -126,4 +131,6 @@ | ||
`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. | ||
`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. It can be either a string or the return value of [`useContextKey`](#usecontextkey). | ||
All `useContextState` calls with the same `contextKey` throughout components in the [`Provider`](#provider) tree will share the same state. | ||
```jsx | ||
@@ -157,4 +164,4 @@ import { useContextState } from "constate"; | ||
function useCounter(context) { | ||
const [count, setCount] = useContextState(context, 0); | ||
function useCounter(key) { | ||
const [count, setCount] = useContextState(key, 0); | ||
const increment = () => setCount(count + 1); | ||
@@ -181,3 +188,3 @@ return { count, increment }; | ||
Just like [`useContextState`](#usecontextstate), `useContextReducer` works similarly to [`React.useReducer`](https://reactjs.org/docs/hooks-reference.html#usereducer), but accepting a `contextKey` argument: | ||
Just like [`useContextState`](#usecontextstate), `useContextReducer` works similarly to [`React.useReducer`](https://reactjs.org/docs/hooks-reference.html#usereducer), but accepting a `contextKey` argument, which can be either a string or the return value of [`useContextKey`](#usecontextkey): | ||
@@ -195,4 +202,4 @@ ```jsx | ||
function useCounter(context) { | ||
const [count, dispatch] = useContextReducer(context, reducer, 0); | ||
function useCounter(key) { | ||
const [count, dispatch] = useContextReducer(key, reducer, 0); | ||
const increment = () => dispatch({ type: "INCREMENT" }); | ||
@@ -211,2 +218,69 @@ const decrement = () => dispatch({ type: "DECREMENT" }); | ||
## `useContextKey` | ||
<sup><a href="#table-of-contents">↑ Back to top</a></sup> | ||
Instead of passing strings to [`useContextState`](#usecontextstate) and [`useContextReducer`](#usecontextreducer), you can create a reference to the context key. | ||
```js | ||
import { useContextKey } from "constate"; | ||
function Counter() { | ||
const key = useContextKey("counter1"); | ||
const [count, setCount] = useContextState(key, 0); | ||
... | ||
} | ||
``` | ||
It uses [`React.useRef`](https://reactjs.org/docs/hooks-reference.html#useref) underneath and is required when using [`useContextEffect`](#usecontexteffect). | ||
<br> | ||
## `useContextEffect` | ||
<sup><a href="#table-of-contents">↑ Back to top</a></sup> | ||
Constate provides all contextual versions of [`React.useEffect`](https://reactjs.org/docs/hooks-reference.html#useeffect), such as `useContextEffect`, `useContextMutationEffect` and `useContextLayoutEffect`. | ||
They receive `contextKey` as the first argument. Unless [`useContextState`](#usecontextstate) and [`useContextReducer`](#usecontextreducer), it's limited to the value returned by [`useContextKey`](#usecontextkey). If `contextKey` is `null` or `undefined`, the hook will work exactly as the React one. | ||
```js | ||
import { Provider, useContextKey, useContextEffect } from "constate"; | ||
let count = 0; | ||
function useCounter(context) { | ||
// useContextKey is required for effects | ||
const key = useContextKey(context); | ||
useContextEffect(key, () => { | ||
count += 1; | ||
}, []); | ||
} | ||
function ContextualCounter1() { | ||
useCounter("counter1"); | ||
... | ||
} | ||
function ContextualCounter2() { | ||
useCounter("counter1"); | ||
... | ||
} | ||
function App() { | ||
return ( | ||
<Provider> | ||
<ContextualCounter1 /> | ||
<ContextualCounter2 /> | ||
</Provider> | ||
); | ||
} | ||
``` | ||
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`. | ||
`useContextEffect` ensures that the function will be called only once per `contextKey` no matter how many components are using it. | ||
<br> | ||
## `createContext` | ||
@@ -222,3 +296,11 @@ | ||
const { Provider, useContextState, useContextReducer } = createContext({ | ||
const { | ||
Provider, | ||
useContextKey, | ||
useContextState, | ||
useContextReducer, | ||
useContextEffect, | ||
useContextLayoutEffect, | ||
useContextMutationEffect | ||
} = createContext({ | ||
counter1: 0, | ||
@@ -230,3 +312,11 @@ posts: [ | ||
export { Provider, useContextState, useContextReducer }; | ||
export { | ||
Provider, | ||
useContextKey, | ||
useContextState, | ||
useContextReducer, | ||
useContextEffect, | ||
useContextLayoutEffect, | ||
useContextMutationEffect | ||
}; | ||
``` | ||
@@ -233,0 +323,0 @@ |
import * as React from "react"; | ||
// I'm unemployed anyway | ||
function getCurrentOwner() { | ||
return (React as any).__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED | ||
.ReactCurrentOwner.current; | ||
} | ||
function createUseContextEffect<State>( | ||
type: "useEffect" | "useMutationEffect" | "useLayoutEffect" = "useEffect" | ||
) { | ||
const consumers: { [key: string]: React.MutableRefObject<any> | null } = {}; | ||
const consumers: { | ||
[K in keyof State]?: React.MutableRefObject<K> | null | ||
} = {}; | ||
return function useContextEffect( | ||
contextKey: keyof State | undefined | null, | ||
contextKey: React.MutableRefObject<keyof State> | null | undefined, | ||
create: () => void | (() => void), | ||
inputs?: ReadonlyArray<any> | ||
) { | ||
const key = contextKey as string; | ||
const consumer = React.useRef(getCurrentOwner()); | ||
if (consumers[key] == null) { | ||
consumers[key] = consumer.current; | ||
const key = contextKey ? contextKey.current : contextKey; | ||
if (key && consumers[key] == null) { | ||
consumers[key] = contextKey!; | ||
} | ||
React.useMutationEffect( | ||
() => { | ||
if (!key) return undefined; | ||
return () => { | ||
() => () => { | ||
if (key && consumers[key] === contextKey) { | ||
consumers[key] = null; | ||
}; | ||
} | ||
}, | ||
@@ -37,3 +32,3 @@ [key] | ||
() => { | ||
if (!key || consumers[key] === consumer.current) { | ||
if (!key || consumers[key] === contextKey) { | ||
return create(); | ||
@@ -40,0 +35,0 @@ } |
@@ -6,3 +6,3 @@ import * as React from "react"; | ||
<K extends keyof State, Action>( | ||
contextKey: K | undefined | null, | ||
contextKey: React.MutableRefObject<K> | K | undefined | null, | ||
reducer: Reducer<State[K], Action>, | ||
@@ -14,3 +14,3 @@ initialState?: null, | ||
<K extends keyof State, S extends State[K], Action>( | ||
contextKey: K | undefined | null, | ||
contextKey: React.MutableRefObject<K> | K | undefined | null, | ||
reducer: Reducer<S, Action>, | ||
@@ -29,3 +29,7 @@ initialState?: S | (() => S) | null, | ||
return function useContextReducer( | ||
contextKey: keyof State | undefined | null, | ||
contextKey: | ||
| React.MutableRefObject<keyof State> | ||
| keyof State | ||
| undefined | ||
| null, | ||
reducer: Reducer<State[keyof State], any>, | ||
@@ -35,6 +39,11 @@ initialState?: State[keyof State], | ||
) { | ||
const key = | ||
typeof contextKey === "object" && contextKey | ||
? contextKey.current | ||
: contextKey; | ||
// @ts-ignore | ||
const [contextState, setContextState] = React.useContext( | ||
contextKey ? context : EmptyContext, | ||
contextKey ? hash(contextKey as string) : undefined | ||
key ? context : EmptyContext, | ||
key ? hash(key as string) : undefined | ||
); | ||
@@ -48,5 +57,5 @@ | ||
if (contextKey) { | ||
if (contextState[contextKey] != null) { | ||
state = contextState[contextKey]; | ||
if (key) { | ||
if (contextState[key] != null) { | ||
state = contextState[key]; | ||
} | ||
@@ -57,3 +66,3 @@ | ||
Object.assign({}, prevState, { | ||
[contextKey]: reducer(prevState[contextKey], action) | ||
[key]: reducer(prevState[key], action) | ||
}) | ||
@@ -65,7 +74,7 @@ ); | ||
() => { | ||
if (contextKey && contextState[contextKey] == null && state != null) { | ||
if (key && contextState[key] == null && state != null) { | ||
setContextState((prevState: State) => { | ||
if (prevState[contextKey] == null) { | ||
if (prevState[key] == null) { | ||
return Object.assign({}, prevState, { | ||
[contextKey]: state | ||
[key]: state | ||
}); | ||
@@ -77,3 +86,3 @@ } | ||
}, | ||
[contextKey] | ||
[key] | ||
); | ||
@@ -80,0 +89,0 @@ |
@@ -6,6 +6,8 @@ import * as React from "react"; | ||
export interface UseContextState<State> { | ||
<K extends keyof State>(contextKey?: K | null): ContextState<State[K]>; | ||
<K extends keyof State>( | ||
contextKey?: React.MutableRefObject<K> | K | null | ||
): ContextState<State[K]>; | ||
<K extends keyof State, S extends State[K]>( | ||
contextKey?: K | null, | ||
contextKey?: React.MutableRefObject<K> | K | null, | ||
initialState?: S | (() => S) | null | ||
@@ -12,0 +14,0 @@ ): ContextState<S>; |
@@ -8,5 +8,6 @@ import createContext from "./createContext"; | ||
useContextState, | ||
unstable_useContextEffect, | ||
unstable_useContextLayoutEffect, | ||
unstable_useContextMutationEffect | ||
useContextKey, | ||
useContextEffect, | ||
useContextLayoutEffect, | ||
useContextMutationEffect | ||
} = createContext<{ [key: string]: any }>({}); | ||
@@ -20,5 +21,6 @@ | ||
useContextState, | ||
unstable_useContextEffect, | ||
unstable_useContextLayoutEffect, | ||
unstable_useContextMutationEffect | ||
useContextKey, | ||
useContextEffect, | ||
useContextLayoutEffect, | ||
useContextMutationEffect | ||
}; |
@@ -10,3 +10,3 @@ import * as React from "react"; | ||
setState: SetState<State>, | ||
{ enabled = true }: { enabled?: boolean } = {} | ||
{ enabled }: { enabled?: boolean } = {} | ||
) { | ||
@@ -13,0 +13,0 @@ const devtools = React.useRef<ReturnType< |
Sorry, the diff of this file is not supported yet
51603
28
782
351