diff --git a/CHANGELOG.md b/CHANGELOG.md index caf09f1..b10da8a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ # Change Log ## [Unreleased] +### Changed +- Direct state context + - No breaking change in API, but it may behave differently +- Support custom context ## [3.0.0] - 2019-05-18 ### Changed diff --git a/dist/ReduxProvider.js b/dist/ReduxProvider.js new file mode 100644 index 0000000..29a8820 --- /dev/null +++ b/dist/ReduxProvider.js @@ -0,0 +1,87 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.ReduxProvider = exports.defaultContext = exports.createCustomContext = void 0; + +var _react = require("react"); + +var _utils = require("./utils"); + +// ------------------------------------------------------- +// context +// ------------------------------------------------------- +var warningObject = { + get state() { + throw new Error('Please use '); + }, + + get dispatch() { + throw new Error('Please use '); + }, + + get subscribe() { + throw new Error('Please use '); + } + +}; + +var calculateChangedBits = function calculateChangedBits(a, b) { + return a.dispatch !== b.dispatch || a.subscribe !== b.subscribe ? 1 : 0; +}; + +var createCustomContext = function createCustomContext() { + var w = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : warningObject; + var c = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : calculateChangedBits; + return (0, _react.createContext)(w, c); +}; + +exports.createCustomContext = createCustomContext; +var defaultContext = createCustomContext(); // ------------------------------------------------------- +// provider +// ------------------------------------------------------- + +exports.defaultContext = defaultContext; + +var ReduxProvider = function ReduxProvider(_ref) { + var store = _ref.store, + _ref$customContext = _ref.customContext, + customContext = _ref$customContext === void 0 ? defaultContext : _ref$customContext, + children = _ref.children; + var forceUpdate = (0, _utils.useForceUpdate)(); + var state = store.getState(); + var listeners = (0, _react.useRef)([]); + (0, _utils.useIsomorphicLayoutEffect)(function () { + listeners.current.forEach(function (listener) { + return listener(state); + }); + }, [state]); + var subscribe = (0, _react.useCallback)(function (listener) { + listeners.current.push(listener); + + var unsubscribe = function unsubscribe() { + var index = listeners.current.indexOf(listener); + listeners.current.splice(index, 1); + }; // run once in case the state is already changed + + + listener(store.getState()); + return unsubscribe; + }, [store]); + (0, _react.useEffect)(function () { + var unsubscribe = store.subscribe(function () { + forceUpdate(); + }); + return unsubscribe; + }, [store, forceUpdate]); + return (0, _react.createElement)(customContext.Provider, { + value: { + state: state, + dispatch: store.dispatch, + subscribe: subscribe + } + }, children); +}; + +exports.ReduxProvider = ReduxProvider; \ No newline at end of file diff --git a/dist/index.js b/dist/index.js index 5fb6746..b0a4e7c 100644 --- a/dist/index.js +++ b/dist/index.js @@ -6,7 +6,13 @@ Object.defineProperty(exports, "__esModule", { Object.defineProperty(exports, "ReduxProvider", { enumerable: true, get: function get() { - return _provider.ReduxProvider; + return _ReduxProvider.ReduxProvider; + } +}); +Object.defineProperty(exports, "createCustomContext", { + enumerable: true, + get: function get() { + return _ReduxProvider.createCustomContext; } }); Object.defineProperty(exports, "useReduxDispatch", { @@ -40,7 +46,7 @@ Object.defineProperty(exports, "useReduxSelectors", { } }); -var _provider = require("./provider"); +var _ReduxProvider = require("./ReduxProvider"); var _useReduxDispatch = require("./useReduxDispatch"); diff --git a/dist/provider.js b/dist/provider.js deleted file mode 100644 index 9a99a24..0000000 --- a/dist/provider.js +++ /dev/null @@ -1,76 +0,0 @@ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.ReduxProvider = exports.ReduxStoreContext = void 0; - -var _react = require("react"); - -var _batchedUpdates = require("./batchedUpdates"); - -function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; } - -function _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; } - -// context -var warningObject = { - get dispatch() { - throw new Error('Please use '); - }, - - get getState() { - throw new Error('Please use '); - } - -}; -var ReduxStoreContext = (0, _react.createContext)(warningObject); // patch store with batchedUpdates - -exports.ReduxStoreContext = ReduxStoreContext; - -var patchReduxStore = function patchReduxStore(origStore) { - if (!_batchedUpdates.batchedUpdates) return origStore; - var listeners = []; - var unsubscribe; - - var subscribe = function subscribe(listener) { - listeners.push(listener); - - if (listeners.length === 1) { - unsubscribe = origStore.subscribe(function () { - (0, _batchedUpdates.batchedUpdates)(function () { - listeners.forEach(function (l) { - return l(); - }); - }); - }); - } - - return function () { - var index = listeners.indexOf(listener); - listeners.splice(index, 1); - - if (listeners.length === 0) { - unsubscribe(); - } - }; - }; - - return _objectSpread({}, origStore, { - subscribe: subscribe - }); -}; // provider - - -var ReduxProvider = function ReduxProvider(_ref) { - var store = _ref.store, - children = _ref.children; - var patchedStore = (0, _react.useMemo)(function () { - return patchReduxStore(store); - }, [store]); - return (0, _react.createElement)(ReduxStoreContext.Provider, { - value: patchedStore - }, children); -}; - -exports.ReduxProvider = ReduxProvider; \ No newline at end of file diff --git a/dist/useReduxDispatch.js b/dist/useReduxDispatch.js index 3513988..1ae6966 100644 --- a/dist/useReduxDispatch.js +++ b/dist/useReduxDispatch.js @@ -7,11 +7,17 @@ exports.useReduxDispatch = void 0; var _react = require("react"); -var _provider = require("./provider"); +var _ReduxProvider = require("./ReduxProvider"); var useReduxDispatch = function useReduxDispatch() { - var store = (0, _react.useContext)(_provider.ReduxStoreContext); - return store.dispatch; + var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + var _opts$customContext = opts.customContext, + customContext = _opts$customContext === void 0 ? _ReduxProvider.defaultContext : _opts$customContext; + + var _useContext = (0, _react.useContext)(customContext), + dispatch = _useContext.dispatch; + + return dispatch; }; exports.useReduxDispatch = useReduxDispatch; \ No newline at end of file diff --git a/dist/useReduxSelectors.js b/dist/useReduxSelectors.js index 993672a..3d861b1 100644 --- a/dist/useReduxSelectors.js +++ b/dist/useReduxSelectors.js @@ -11,7 +11,7 @@ var _memoizeState = _interopRequireDefault(require("memoize-state")); var _withKnownUsage = require("with-known-usage"); -var _provider = require("./provider"); +var _ReduxProvider = require("./ReduxProvider"); var _utils = require("./utils"); @@ -62,10 +62,15 @@ var runSelector = function runSelector(state, selector) { }; var useReduxSelectors = function useReduxSelectors(selectorMap) { - var forceUpdate = (0, _utils.useForceUpdate)(); // redux store&state + var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + var _opts$customContext = opts.customContext, + customContext = _opts$customContext === void 0 ? _ReduxProvider.defaultContext : _opts$customContext; + var forceUpdate = (0, _utils.useForceUpdate)(); // redux state + + var _useContext = (0, _react.useContext)(customContext), + state = _useContext.state, + subscribe = _useContext.subscribe; // mapped result - var store = (0, _react.useContext)(_provider.ReduxStoreContext); - var state = store.getState(); // mapped result var keys = Object.keys(selectorMap); var mapped = createMap(keys, function (key) { @@ -85,9 +90,8 @@ var useReduxSelectors = function useReduxSelectors(selectorMap) { }); // subscription (0, _react.useEffect)(function () { - var callback = function callback() { + var callback = function callback(nextState) { try { - var nextState = store.getState(); var changed = false; var nextMapped = createMap(lastTracked.current.keys, function (key) { var lastResult = lastTracked.current.mapped[key]; @@ -109,13 +113,11 @@ var useReduxSelectors = function useReduxSelectors(selectorMap) { // detect erorr (probably stale props) forceUpdate(); } - }; // run once in case the state is already changed - + }; - callback(); - var unsubscribe = store.subscribe(callback); + var unsubscribe = subscribe(callback); return unsubscribe; - }, [store, forceUpdate]); + }, [subscribe, forceUpdate]); return trapped.proxy; }; diff --git a/dist/useReduxState.js b/dist/useReduxState.js index 8fe0ddf..5f61984 100644 --- a/dist/useReduxState.js +++ b/dist/useReduxState.js @@ -7,7 +7,7 @@ exports.useReduxState = void 0; var _react = require("react"); -var _provider = require("./provider"); +var _ReduxProvider = require("./ReduxProvider"); var _utils = require("./utils"); @@ -15,9 +15,14 @@ var _deepProxy = require("./deepProxy"); var useReduxState = function useReduxState() { var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + var _opts$customContext = opts.customContext, + customContext = _opts$customContext === void 0 ? _ReduxProvider.defaultContext : _opts$customContext; var forceUpdate = (0, _utils.useForceUpdate)(); - var store = (0, _react.useContext)(_provider.ReduxStoreContext); - var state = store.getState(); + + var _useContext = (0, _react.useContext)(customContext), + state = _useContext.state, + subscribe = _useContext.subscribe; + var affected = new WeakMap(); var lastTracked = (0, _react.useRef)(null); (0, _utils.useIsomorphicLayoutEffect)(function () { @@ -35,21 +40,18 @@ var useReduxState = function useReduxState() { }; }); (0, _react.useEffect)(function () { - var callback = function callback() { - var nextState = store.getState(); + var callback = function callback(nextState) { var changed = (0, _deepProxy.isDeepChanged)(lastTracked.current.state, nextState, lastTracked.current.affected, lastTracked.current.cache, lastTracked.current.assumeChangedIfNotAffected); if (changed) { lastTracked.current.state = nextState; forceUpdate(); } - }; // run once in case the state is already changed - + }; - callback(); - var unsubscribe = store.subscribe(callback); + var unsubscribe = subscribe(callback); return unsubscribe; - }, [store, forceUpdate]); + }, [subscribe, forceUpdate]); var proxyCache = (0, _react.useRef)(new WeakMap()); // per-hook proxyCache return (0, _deepProxy.createDeepProxy)(state, affected, proxyCache.current); diff --git a/dist/useReduxStateRich.js b/dist/useReduxStateRich.js index c631289..dbfb2e6 100644 --- a/dist/useReduxStateRich.js +++ b/dist/useReduxStateRich.js @@ -9,7 +9,7 @@ var _react = require("react"); var _proxyequal = require("proxyequal"); -var _provider = require("./provider"); +var _ReduxProvider = require("./ReduxProvider"); var _utils = require("./utils"); @@ -35,10 +35,15 @@ var useTrapped = function useTrapped(state) { }; var useReduxStateRich = function useReduxStateRich() { - var forceUpdate = (0, _utils.useForceUpdate)(); // redux store&state + var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + var _opts$customContext = opts.customContext, + customContext = _opts$customContext === void 0 ? _ReduxProvider.defaultContext : _opts$customContext; + var forceUpdate = (0, _utils.useForceUpdate)(); // redux state + + var _useContext = (0, _react.useContext)(customContext), + state = _useContext.state, + subscribe = _useContext.subscribe; // trapped - var store = (0, _react.useContext)(_provider.ReduxStoreContext); - var state = store.getState(); // trapped var trapped = useTrapped(state); // ref @@ -51,21 +56,18 @@ var useReduxStateRich = function useReduxStateRich() { }); // subscription (0, _react.useEffect)(function () { - var callback = function callback() { - var nextState = store.getState(); + var callback = function callback(nextState) { var changed = !(0, _proxyequal.proxyEqual)(lastTracked.current.state, nextState, lastTracked.current.affected); if (changed) { lastTracked.current.state = nextState; forceUpdate(); } - }; // run once in case the state is already changed - + }; - callback(); - var unsubscribe = store.subscribe(callback); + var unsubscribe = subscribe(callback); return unsubscribe; - }, [store, forceUpdate]); + }, [subscribe, forceUpdate]); return trapped.state; }; diff --git a/dist/useReduxStateSimple.js b/dist/useReduxStateSimple.js index add001f..3b6d4bf 100644 --- a/dist/useReduxStateSimple.js +++ b/dist/useReduxStateSimple.js @@ -7,7 +7,7 @@ exports.useReduxStateSimple = void 0; var _react = require("react"); -var _provider = require("./provider"); +var _ReduxProvider = require("./ReduxProvider"); var _utils = require("./utils"); @@ -15,8 +15,15 @@ var _utils = require("./utils"); // simple version: one depth comparison // ------------------------------------------------------- var useReduxStateSimple = function useReduxStateSimple() { + var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + var _opts$customContext = opts.customContext, + customContext = _opts$customContext === void 0 ? _ReduxProvider.defaultContext : _opts$customContext; var forceUpdate = (0, _utils.useForceUpdate)(); - var store = (0, _react.useContext)(_provider.ReduxStoreContext); + + var _useContext = (0, _react.useContext)(customContext), + state = _useContext.state, + subscribe = _useContext.subscribe; + var used = (0, _react.useRef)({}); var handler = (0, _react.useMemo)(function () { return { @@ -26,14 +33,12 @@ var useReduxStateSimple = function useReduxStateSimple() { } }; }, []); - var state = store.getState(); var lastState = (0, _react.useRef)(null); (0, _utils.useIsomorphicLayoutEffect)(function () { lastState.current = state; }); (0, _react.useEffect)(function () { - var callback = function callback() { - var nextState = store.getState(); + var callback = function callback(nextState) { var changed = Object.keys(used.current).find(function (key) { return lastState.current[key] !== nextState[key]; }); @@ -41,11 +46,9 @@ var useReduxStateSimple = function useReduxStateSimple() { if (changed) { forceUpdate(); } - }; // run once in case the state is already changed - + }; - callback(); - var unsubscribe = store.subscribe(callback); + var unsubscribe = subscribe(callback); var cleanup = function cleanup() { unsubscribe(); @@ -53,7 +56,7 @@ var useReduxStateSimple = function useReduxStateSimple() { }; return cleanup; - }, [store, forceUpdate]); + }, [subscribe, forceUpdate]); return new Proxy(state, handler); }; diff --git a/package-lock.json b/package-lock.json index aa4ca1a..7b4b110 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "reactive-react-redux", - "version": "3.0.0", + "version": "4.0.0-alpha.2", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index bc77b94..e767118 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "reactive-react-redux", "description": "React Redux binding with React Hooks and Proxy", - "version": "3.0.0", + "version": "4.0.0-alpha.2", "author": "Daishi Kato", "repository": { "type": "git", diff --git a/src/ReduxProvider.js b/src/ReduxProvider.js new file mode 100644 index 0000000..0ba21f9 --- /dev/null +++ b/src/ReduxProvider.js @@ -0,0 +1,74 @@ +import { + createContext, + createElement, + useCallback, + useEffect, + useRef, +} from 'react'; + +import { useIsomorphicLayoutEffect, useForceUpdate } from './utils'; + +// ------------------------------------------------------- +// context +// ------------------------------------------------------- + +const warningObject = { + get state() { + throw new Error('Please use '); + }, + get dispatch() { + throw new Error('Please use '); + }, + get subscribe() { + throw new Error('Please use '); + }, +}; + +const calculateChangedBits = (a, b) => ( + a.dispatch !== b.dispatch || a.subscribe !== b.subscribe ? 1 : 0 +); + +export const createCustomContext = ( + w = warningObject, + c = calculateChangedBits, +) => createContext(w, c); + +export const defaultContext = createCustomContext(); + +// ------------------------------------------------------- +// provider +// ------------------------------------------------------- + +export const ReduxProvider = ({ + store, + customContext = defaultContext, + children, +}) => { + const forceUpdate = useForceUpdate(); + const state = store.getState(); + const listeners = useRef([]); + useIsomorphicLayoutEffect(() => { + listeners.current.forEach(listener => listener(state)); + }, [state]); + const subscribe = useCallback((listener) => { + listeners.current.push(listener); + const unsubscribe = () => { + const index = listeners.current.indexOf(listener); + listeners.current.splice(index, 1); + }; + // run once in case the state is already changed + listener(store.getState()); + return unsubscribe; + }, [store]); + useEffect(() => { + const unsubscribe = store.subscribe(() => { + forceUpdate(); + }); + return unsubscribe; + }, [store, forceUpdate]); + return createElement( + customContext.Provider, + { value: { state, dispatch: store.dispatch, subscribe } }, + children, + ); +}; diff --git a/src/index.d.ts b/src/index.d.ts index 63a0fec..278c5ce 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -6,8 +6,13 @@ import { Store, } from 'redux'; +type CustomContext = React.Context; + +export type createCustomContext = () => CustomContext; + export type ReduxProviderProps = { store: Store; + customContext?: CustomContext; }; export type ReduxProviderType @@ -17,11 +22,16 @@ export const ReduxProvider: ReduxProviderType; export const useReduxDispatch: () => Dispatch; -export const useReduxState: () => S; +type Opts = { + customContext?: CustomContext; +}; + +export const useReduxState: (opts?: Opts) => S; export const useReduxSelectors: ( selectors: { [K in keyof M]: (state: S) => M[K] }, + opts?: Opts, ) => M; -export const useReduxStateSimple: () => S; -export const useReduxStateRich: () => S; +export const useReduxStateSimple: (opts?: Opts) => S; +export const useReduxStateRich: (opts?: Opts) => S; diff --git a/src/index.js b/src/index.js index fd61459..a2d2841 100644 --- a/src/index.js +++ b/src/index.js @@ -1,4 +1,4 @@ -export { ReduxProvider } from './provider'; +export { ReduxProvider, createCustomContext } from './ReduxProvider'; export { useReduxDispatch } from './useReduxDispatch'; export { useReduxState } from './useReduxState'; diff --git a/src/provider.js b/src/provider.js deleted file mode 100644 index 751deb2..0000000 --- a/src/provider.js +++ /dev/null @@ -1,52 +0,0 @@ -import { createContext, createElement, useMemo } from 'react'; - -import { batchedUpdates } from './batchedUpdates'; - -// context -const warningObject = { - get dispatch() { - throw new Error('Please use '); - }, - get getState() { - throw new Error('Please use '); - }, -}; -export const ReduxStoreContext = createContext(warningObject); - -// patch store with batchedUpdates -const patchReduxStore = (origStore) => { - if (!batchedUpdates) return origStore; - const listeners = []; - let unsubscribe; - const subscribe = (listener) => { - listeners.push(listener); - if (listeners.length === 1) { - unsubscribe = origStore.subscribe(() => { - batchedUpdates(() => { - listeners.forEach(l => l()); - }); - }); - } - return () => { - const index = listeners.indexOf(listener); - listeners.splice(index, 1); - if (listeners.length === 0) { - unsubscribe(); - } - }; - }; - return { - ...origStore, - subscribe, - }; -}; - -// provider -export const ReduxProvider = ({ store, children }) => { - const patchedStore = useMemo(() => patchReduxStore(store), [store]); - return createElement( - ReduxStoreContext.Provider, - { value: patchedStore }, - children, - ); -}; diff --git a/src/useReduxDispatch.js b/src/useReduxDispatch.js index 9ad6c70..d624dbf 100644 --- a/src/useReduxDispatch.js +++ b/src/useReduxDispatch.js @@ -1,8 +1,11 @@ import { useContext } from 'react'; -import { ReduxStoreContext } from './provider'; +import { defaultContext } from './ReduxProvider'; -export const useReduxDispatch = () => { - const store = useContext(ReduxStoreContext); - return store.dispatch; +export const useReduxDispatch = (opts = {}) => { + const { + customContext = defaultContext, + } = opts; + const { dispatch } = useContext(customContext); + return dispatch; }; diff --git a/src/useReduxSelectors.js b/src/useReduxSelectors.js index 3a4ed33..aa4e533 100644 --- a/src/useReduxSelectors.js +++ b/src/useReduxSelectors.js @@ -8,7 +8,7 @@ import memoize from 'memoize-state'; import { withKnowUsage } from 'with-known-usage'; -import { ReduxStoreContext } from './provider'; +import { defaultContext } from './ReduxProvider'; import { useIsomorphicLayoutEffect, useForceUpdate } from './utils'; @@ -48,11 +48,13 @@ const runSelector = (state, selector) => { return { selector, value }; }; -export const useReduxSelectors = (selectorMap) => { +export const useReduxSelectors = (selectorMap, opts = {}) => { + const { + customContext = defaultContext, + } = opts; const forceUpdate = useForceUpdate(); - // redux store&state - const store = useContext(ReduxStoreContext); - const state = store.getState(); + // redux state + const { state, subscribe } = useContext(customContext); // mapped result const keys = Object.keys(selectorMap); const mapped = createMap(keys, key => runSelector(state, selectorMap[key])); @@ -64,9 +66,8 @@ export const useReduxSelectors = (selectorMap) => { }); // subscription useEffect(() => { - const callback = () => { + const callback = (nextState) => { try { - const nextState = store.getState(); let changed = false; const nextMapped = createMap(lastTracked.current.keys, (key) => { const lastResult = lastTracked.current.mapped[key]; @@ -86,10 +87,8 @@ export const useReduxSelectors = (selectorMap) => { forceUpdate(); } }; - // run once in case the state is already changed - callback(); - const unsubscribe = store.subscribe(callback); + const unsubscribe = subscribe(callback); return unsubscribe; - }, [store, forceUpdate]); + }, [subscribe, forceUpdate]); return trapped.proxy; }; diff --git a/src/useReduxState.js b/src/useReduxState.js index 7c97bab..7c5fc39 100644 --- a/src/useReduxState.js +++ b/src/useReduxState.js @@ -4,16 +4,18 @@ import { useRef, } from 'react'; -import { ReduxStoreContext } from './provider'; +import { defaultContext } from './ReduxProvider'; import { useIsomorphicLayoutEffect, useForceUpdate } from './utils'; import { createDeepProxy, isDeepChanged } from './deepProxy'; export const useReduxState = (opts = {}) => { + const { + customContext = defaultContext, + } = opts; const forceUpdate = useForceUpdate(); - const store = useContext(ReduxStoreContext); - const state = store.getState(); + const { state, subscribe } = useContext(customContext); const affected = new WeakMap(); const lastTracked = useRef(null); useIsomorphicLayoutEffect(() => { @@ -30,8 +32,7 @@ export const useReduxState = (opts = {}) => { }; }); useEffect(() => { - const callback = () => { - const nextState = store.getState(); + const callback = (nextState) => { const changed = isDeepChanged( lastTracked.current.state, nextState, @@ -44,11 +45,9 @@ export const useReduxState = (opts = {}) => { forceUpdate(); } }; - // run once in case the state is already changed - callback(); - const unsubscribe = store.subscribe(callback); + const unsubscribe = subscribe(callback); return unsubscribe; - }, [store, forceUpdate]); + }, [subscribe, forceUpdate]); const proxyCache = useRef(new WeakMap()); // per-hook proxyCache return createDeepProxy(state, affected, proxyCache.current); }; diff --git a/src/useReduxStateRich.js b/src/useReduxStateRich.js index f5bf69c..31bd2ff 100644 --- a/src/useReduxStateRich.js +++ b/src/useReduxStateRich.js @@ -6,7 +6,7 @@ import { import { proxyState, proxyEqual } from 'proxyequal'; -import { ReduxStoreContext } from './provider'; +import { defaultContext } from './ReduxProvider'; import { useIsomorphicLayoutEffect, useForceUpdate } from './utils'; @@ -30,11 +30,13 @@ const useTrapped = (state) => { return trapped; }; -export const useReduxStateRich = () => { +export const useReduxStateRich = (opts = {}) => { + const { + customContext = defaultContext, + } = opts; const forceUpdate = useForceUpdate(); - // redux store&state - const store = useContext(ReduxStoreContext); - const state = store.getState(); + // redux state + const { state, subscribe } = useContext(customContext); // trapped const trapped = useTrapped(state); // ref @@ -47,8 +49,7 @@ export const useReduxStateRich = () => { }); // subscription useEffect(() => { - const callback = () => { - const nextState = store.getState(); + const callback = (nextState) => { const changed = !proxyEqual( lastTracked.current.state, nextState, @@ -59,10 +60,8 @@ export const useReduxStateRich = () => { forceUpdate(); } }; - // run once in case the state is already changed - callback(); - const unsubscribe = store.subscribe(callback); + const unsubscribe = subscribe(callback); return unsubscribe; - }, [store, forceUpdate]); + }, [subscribe, forceUpdate]); return trapped.state; }; diff --git a/src/useReduxStateSimple.js b/src/useReduxStateSimple.js index a365818..6ed8844 100644 --- a/src/useReduxStateSimple.js +++ b/src/useReduxStateSimple.js @@ -5,7 +5,7 @@ import { useRef, } from 'react'; -import { ReduxStoreContext } from './provider'; +import { defaultContext } from './ReduxProvider'; import { useIsomorphicLayoutEffect, useForceUpdate } from './utils'; @@ -13,9 +13,12 @@ import { useIsomorphicLayoutEffect, useForceUpdate } from './utils'; // simple version: one depth comparison // ------------------------------------------------------- -export const useReduxStateSimple = () => { +export const useReduxStateSimple = (opts = {}) => { + const { + customContext = defaultContext, + } = opts; const forceUpdate = useForceUpdate(); - const store = useContext(ReduxStoreContext); + const { state, subscribe } = useContext(customContext); const used = useRef({}); const handler = useMemo(() => ({ get: (target, name) => { @@ -23,14 +26,12 @@ export const useReduxStateSimple = () => { return target[name]; }, }), []); - const state = store.getState(); const lastState = useRef(null); useIsomorphicLayoutEffect(() => { lastState.current = state; }); useEffect(() => { - const callback = () => { - const nextState = store.getState(); + const callback = (nextState) => { const changed = Object.keys(used.current).find( key => lastState.current[key] !== nextState[key], ); @@ -38,14 +39,12 @@ export const useReduxStateSimple = () => { forceUpdate(); } }; - // run once in case the state is already changed - callback(); - const unsubscribe = store.subscribe(callback); + const unsubscribe = subscribe(callback); const cleanup = () => { unsubscribe(); used.current = {}; }; return cleanup; - }, [store, forceUpdate]); + }, [subscribe, forceUpdate]); return new Proxy(state, handler); };