@easy-peasy/react
Version:
easy-peasy connector for React
222 lines (190 loc) • 7.23 kB
JavaScript
Object.defineProperty(exports, '__esModule', { value: true });
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
var immerPeasy = require('immer-peasy');
var React = require('react');
var React__default = _interopDefault(React);
var core = require('@easy-peasy/core');
var StoreContext = React.createContext();
// To get around it, we can conditionally useEffect on the server (no-op) and
// useLayoutEffect in the browser. We need useLayoutEffect to ensure the store
// subscription callback always has the selector from the latest render commit
// available, otherwise a store update may happen between render and the effect,
// which may cause missed updates; we also must ensure the store subscription
// is created synchronously, otherwise a store update may occur before the
// subscription is created and an inconsistent state may be observed
var useIsomorphicLayoutEffect = typeof window !== 'undefined' ? React.useLayoutEffect : React.useEffect;
function createStoreStateHook(Context) {
return function useStoreState(mapState) {
var store = React.useContext(Context);
var mapStateRef = React.useRef(mapState);
var stateRef = React.useRef();
var mountedRef = React.useRef(true);
var subscriptionMapStateError = React.useRef();
var _useReducer = React.useReducer(function (s) {
return s + 1;
}, 0),
forceRender = _useReducer[1];
if (subscriptionMapStateError.current || mapStateRef.current !== mapState || stateRef.current === undefined) {
try {
stateRef.current = mapState(store.getState());
} catch (err) {
if (process.env.NODE_ENV === 'development') {
var errorMessage = "An error occurred trying to map state in a useStoreState hook: " + err.message + ".";
if (subscriptionMapStateError.current) {
errorMessage += "\nThis error may be related to the following error:\n" + subscriptionMapStateError.current.stack + "\n\nOriginal stack trace:";
}
throw new Error(errorMessage);
}
throw subscriptionMapStateError.current || err;
}
}
useIsomorphicLayoutEffect(function () {
mapStateRef.current = mapState;
subscriptionMapStateError.current = undefined;
});
useIsomorphicLayoutEffect(function () {
var checkMapState = function checkMapState() {
try {
var newState = mapStateRef.current(store.getState());
if (newState === stateRef.current) {
return;
}
stateRef.current = newState;
} catch (err) {
// see https://github.com/reduxjs/react-redux/issues/1179
// There is a possibility mapState will fail due to stale state or
// props, therefore we will just track the error and force our
// component to update. It should then receive the updated state
subscriptionMapStateError.current = err;
}
if (mountedRef.current) {
forceRender({});
}
};
var unsubscribe = store.subscribe(checkMapState);
checkMapState();
return function () {
mountedRef.current = false;
unsubscribe();
};
}, []);
return stateRef.current;
};
}
var useStoreState = createStoreStateHook(StoreContext);
function createStoreActionsHook(Context) {
return function useStoreActions(mapActions) {
var store = React.useContext(Context);
return mapActions(store.getActions());
};
}
var useStoreActions = createStoreActionsHook(StoreContext);
function createStoreDispatchHook(Context) {
return function useStoreDispatch() {
var store = React.useContext(Context);
return store.dispatch;
};
}
var useStoreDispatch = createStoreDispatchHook(StoreContext);
function useStore() {
return React.useContext(StoreContext);
}
function createStoreRehydratedHook(Context) {
return function useStoreRehydrated() {
var store = React.useContext(Context);
var _useState = React.useState(false),
rehydrated = _useState[0],
setRehydrated = _useState[1];
React.useEffect(function () {
store.persist.resolveRehydration().then(function () {
return setRehydrated(true);
});
}, []);
return rehydrated;
};
}
var useStoreRehydrated = createStoreRehydratedHook(StoreContext);
function createTypedHooks() {
return {
useStoreActions: useStoreActions,
useStoreDispatch: useStoreDispatch,
useStoreState: useStoreState,
useStoreRehydrated: useStoreRehydrated,
useStore: useStore
};
}
/* eslint-disable react/prop-types */
function createContextStore(model, config) {
var StoreContext = React.createContext();
function Provider(_ref) {
var children = _ref.children,
initialData = _ref.initialData;
var store = React.useMemo(function () {
return core.createStore(typeof model === 'function' ? model(initialData) : model, config);
}, []);
return React__default.createElement(StoreContext.Provider, {
value: store
}, children);
}
function useStore() {
return React.useContext(StoreContext);
}
return {
Provider: Provider,
useStore: useStore,
useStoreState: createStoreStateHook(StoreContext),
useStoreActions: createStoreActionsHook(StoreContext),
useStoreDispatch: createStoreDispatchHook(StoreContext),
useStoreRehydrated: createStoreRehydratedHook(StoreContext)
};
}
/**
* Some good references on the topic of reinitialisation:
* - https://github.com/facebook/react/issues/14830
*/
function createComponentStore(model, config) {
return function useLocalStore(initialData) {
var store = React.useMemo(function () {
return core.createStore(typeof model === 'function' ? model(initialData) : model, config);
}, []);
var previousStateRef = React.useRef(store.getState());
var _useState = React.useState(function () {
return store.getState();
}),
currentState = _useState[0],
setCurrentState = _useState[1];
React.useEffect(function () {
return store.subscribe(function () {
var nextState = store.getState();
if (previousStateRef.current !== nextState) {
previousStateRef.current = nextState;
setCurrentState(nextState);
}
});
}, [store]);
return [currentState, store.getActions()];
};
}
function StoreProvider(_ref) {
var children = _ref.children,
store = _ref.store;
return React__default.createElement(StoreContext.Provider, {
value: store
}, children);
}
/**
* The auto freeze feature of immer doesn't seem to work in our testing. We have
* explicitly disabled it to avoid perf issues.
*/
immerPeasy.setAutoFreeze(false);
exports.StoreProvider = StoreProvider;
exports.createComponentStore = createComponentStore;
exports.createContextStore = createContextStore;
exports.createTypedHooks = createTypedHooks;
exports.useStore = useStore;
exports.useStoreActions = useStoreActions;
exports.useStoreDispatch = useStoreDispatch;
exports.useStoreRehydrated = useStoreRehydrated;
exports.useStoreState = useStoreState;
//# sourceMappingURL=easy-peasy-react.cjs.js.map
;