diff options
| author | Dayana31 <[email protected]> | 2022-04-21 17:27:08 -0500 |
|---|---|---|
| committer | Dayana31 <[email protected]> | 2022-04-21 17:27:08 -0500 |
| commit | 67c50667678dd0ce4709b29a854f6a47093a1ac5 (patch) | |
| tree | b6f9f39092ad54bf6b815984d32b37d7c7ca67ab /front/odiparpack/app/utils | |
| parent | 91140b24f0d49a9f89a080ee063e9eb023a4b73a (diff) | |
| parent | e13e630cd6e4fc0b1ff92098a28a770794c7bb9a (diff) | |
| download | DP1_project-67c50667678dd0ce4709b29a854f6a47093a1ac5.tar.gz DP1_project-67c50667678dd0ce4709b29a854f6a47093a1ac5.tar.bz2 DP1_project-67c50667678dd0ce4709b29a854f6a47093a1ac5.zip | |
Merge branch 'gabshr' into dayana
Diffstat (limited to 'front/odiparpack/app/utils')
| -rw-r--r-- | front/odiparpack/app/utils/checkStore.js | 21 | ||||
| -rw-r--r-- | front/odiparpack/app/utils/constants.js | 3 | ||||
| -rw-r--r-- | front/odiparpack/app/utils/history.js | 3 | ||||
| -rw-r--r-- | front/odiparpack/app/utils/injectReducer.js | 45 | ||||
| -rw-r--r-- | front/odiparpack/app/utils/injectSaga.js | 59 | ||||
| -rw-r--r-- | front/odiparpack/app/utils/reducerInjectors.js | 33 | ||||
| -rw-r--r-- | front/odiparpack/app/utils/sagaInjectors.js | 92 | ||||
| -rw-r--r-- | front/odiparpack/app/utils/sagas.js | 15 | ||||
| -rw-r--r-- | front/odiparpack/app/utils/tests/.DS_Store | bin | 0 -> 6148 bytes |
9 files changed, 271 insertions, 0 deletions
diff --git a/front/odiparpack/app/utils/checkStore.js b/front/odiparpack/app/utils/checkStore.js new file mode 100644 index 0000000..610ea0f --- /dev/null +++ b/front/odiparpack/app/utils/checkStore.js @@ -0,0 +1,21 @@ +import { conformsTo, isFunction, isObject } from 'lodash'; +import invariant from 'invariant'; + +/** + * Validate the shape of redux store + */ +export default function checkStore(store) { + const shape = { + dispatch: isFunction, + subscribe: isFunction, + getState: isFunction, + replaceReducer: isFunction, + runSaga: isFunction, + injectedReducers: isObject, + injectedSagas: isObject, + }; + invariant( + conformsTo(store, shape), + '(app/utils...) injectors: Expected a valid redux store', + ); +} diff --git a/front/odiparpack/app/utils/constants.js b/front/odiparpack/app/utils/constants.js new file mode 100644 index 0000000..97ece0f --- /dev/null +++ b/front/odiparpack/app/utils/constants.js @@ -0,0 +1,3 @@ +export const RESTART_ON_REMOUNT = '@@saga-injector/restart-on-remount'; +export const DAEMON = '@@saga-injector/daemon'; +export const ONCE_TILL_UNMOUNT = '@@saga-injector/once-till-unmount'; diff --git a/front/odiparpack/app/utils/history.js b/front/odiparpack/app/utils/history.js new file mode 100644 index 0000000..ee3abb7 --- /dev/null +++ b/front/odiparpack/app/utils/history.js @@ -0,0 +1,3 @@ +import { createBrowserHistory } from 'history'; +const history = createBrowserHistory(); +export default history; diff --git a/front/odiparpack/app/utils/injectReducer.js b/front/odiparpack/app/utils/injectReducer.js new file mode 100644 index 0000000..13833c2 --- /dev/null +++ b/front/odiparpack/app/utils/injectReducer.js @@ -0,0 +1,45 @@ +import React from 'react'; +import hoistNonReactStatics from 'hoist-non-react-statics'; +import { ReactReduxContext } from 'react-redux'; + +import getInjectors from './reducerInjectors'; + +/** + * Dynamically injects a reducer + * + * @param {string} key A key of the reducer + * @param {function} reducer A reducer that will be injected + * + */ +export default ({ key, reducer }) => WrappedComponent => { + class ReducerInjector extends React.Component { + static WrappedComponent = WrappedComponent; + + static contextType = ReactReduxContext; + + static displayName = `withReducer(${WrappedComponent.displayName + || WrappedComponent.name + || 'Component'})`; + + constructor(props, context) { + super(props, context); + + getInjectors(context.store).injectReducer(key, reducer); + } + + render() { + return <WrappedComponent {...this.props} />; + } + } + + return hoistNonReactStatics(ReducerInjector, WrappedComponent); +}; + +const useInjectReducer = ({ key, reducer }) => { + const context = React.useContext(ReactReduxContext); + React.useEffect(() => { + getInjectors(context.store).injectReducer(key, reducer); + }, []); +}; + +export { useInjectReducer }; diff --git a/front/odiparpack/app/utils/injectSaga.js b/front/odiparpack/app/utils/injectSaga.js new file mode 100644 index 0000000..3f8752b --- /dev/null +++ b/front/odiparpack/app/utils/injectSaga.js @@ -0,0 +1,59 @@ +import React from 'react'; +import hoistNonReactStatics from 'hoist-non-react-statics'; +import { ReactReduxContext } from 'react-redux'; + +import getInjectors from './sagaInjectors'; + +/** + * Dynamically injects a saga, passes component's props as saga arguments + * + * @param {string} key A key of the saga + * @param {function} saga A root saga that will be injected + * @param {string} [mode] By default (constants.DAEMON) the saga will be started + * on component mount and never canceled or started again. Another two options: + * - constants.RESTART_ON_REMOUNT — the saga will be started on component mount and + * cancelled with `task.cancel()` on component unmount for improved performance, + * - constants.ONCE_TILL_UNMOUNT — behaves like 'RESTART_ON_REMOUNT' but never runs it again. + * + */ +export default ({ key, saga, mode }) => WrappedComponent => { + class InjectSaga extends React.Component { + static WrappedComponent = WrappedComponent; + + static contextType = ReactReduxContext; + + static displayName = `withSaga(${WrappedComponent.displayName || WrappedComponent.name || 'Component'})`; + + constructor(props, context) { + super(props, context); + + this.injectors = getInjectors(context.store); + + this.injectors.injectSaga(key, { saga, mode }, this.props); + } + + componentWillUnmount() { + this.injectors.ejectSaga(key); + } + + render() { + return <WrappedComponent {...this.props} />; + } + } + + return hoistNonReactStatics(InjectSaga, WrappedComponent); +}; + +const useInjectSaga = ({ key, saga, mode }) => { + const context = React.useContext(ReactReduxContext); + React.useEffect(() => { + const injectors = getInjectors(context.store); + injectors.injectSaga(key, { saga, mode }); + + return () => { + injectors.ejectSaga(key); + }; + }, []); +}; + +export { useInjectSaga }; diff --git a/front/odiparpack/app/utils/reducerInjectors.js b/front/odiparpack/app/utils/reducerInjectors.js new file mode 100644 index 0000000..d664680 --- /dev/null +++ b/front/odiparpack/app/utils/reducerInjectors.js @@ -0,0 +1,33 @@ +import invariant from 'invariant'; +import { isEmpty, isFunction, isString } from 'lodash'; + +import checkStore from './checkStore'; +import createReducer from '../redux/reducers'; + +export function injectReducerFactory(store, isValid) { + return function injectReducer(key, reducer) { + if (!isValid) checkStore(store); + + invariant( + isString(key) && !isEmpty(key) && isFunction(reducer), + '(app/utils...) injectReducer: Expected `reducer` to be a reducer function', + ); + + // Check `store.injectedReducers[key] === reducer` for hot reloading when a key is the same but a reducer is different + if ( + Reflect.has(store.injectedReducers, key) + && store.injectedReducers[key] === reducer + ) return; + + store.injectedReducers[key] = reducer; // eslint-disable-line no-param-reassign + store.replaceReducer(createReducer(store.injectedReducers)); + }; +} + +export default function getInjectors(store) { + checkStore(store); + + return { + injectReducer: injectReducerFactory(store, true), + }; +} diff --git a/front/odiparpack/app/utils/sagaInjectors.js b/front/odiparpack/app/utils/sagaInjectors.js new file mode 100644 index 0000000..edb4626 --- /dev/null +++ b/front/odiparpack/app/utils/sagaInjectors.js @@ -0,0 +1,92 @@ +import invariant from 'invariant'; +import { + isEmpty, isFunction, isString, conformsTo +} from 'lodash'; + +import checkStore from './checkStore'; +import { DAEMON, ONCE_TILL_UNMOUNT, RESTART_ON_REMOUNT } from './constants'; + +const allowedModes = [RESTART_ON_REMOUNT, DAEMON, ONCE_TILL_UNMOUNT]; + +const checkKey = key => invariant( + isString(key) && !isEmpty(key), + '(app/utils...) injectSaga: Expected `key` to be a non empty string', +); + +const checkDescriptor = descriptor => { + const shape = { + saga: isFunction, + mode: mode => isString(mode) && allowedModes.includes(mode), + }; + invariant( + conformsTo(descriptor, shape), + '(app/utils...) injectSaga: Expected a valid saga descriptor', + ); +}; + +export function injectSagaFactory(store, isValid) { + return function injectSaga(key, descriptor = {}, args) { + if (!isValid) checkStore(store); + + const newDescriptor = { + ...descriptor, + mode: descriptor.mode || DAEMON, + }; + const { saga, mode } = newDescriptor; + + checkKey(key); + checkDescriptor(newDescriptor); + + let hasSaga = Reflect.has(store.injectedSagas, key); + + if (process.env.NODE_ENV !== 'production') { + const oldDescriptor = store.injectedSagas[key]; + // enable hot reloading of daemon and once-till-unmount sagas + if (hasSaga && oldDescriptor.saga !== saga) { + oldDescriptor.task.cancel(); + hasSaga = false; + } + } + + if ( + !hasSaga + || (hasSaga && mode !== DAEMON && mode !== ONCE_TILL_UNMOUNT) + ) { + /* eslint-disable no-param-reassign */ + store.injectedSagas[key] = { + ...newDescriptor, + task: store.runSaga(saga, args), + }; + /* eslint-enable no-param-reassign */ + } + }; +} + +export function ejectSagaFactory(store, isValid) { + return function ejectSaga(key) { + if (!isValid) checkStore(store); + + checkKey(key); + + if (Reflect.has(store.injectedSagas, key)) { + const descriptor = store.injectedSagas[key]; + if (descriptor.mode && descriptor.mode !== DAEMON) { + descriptor.task.cancel(); + // Clean up in production; in development we need `descriptor.saga` for hot reloading + if (process.env.NODE_ENV === 'production') { + // Need some value to be able to detect `ONCE_TILL_UNMOUNT` sagas in `injectSaga` + store.injectedSagas[key] = 'done'; // eslint-disable-line no-param-reassign + } + } + } + }; +} + +export default function getInjectors(store) { + checkStore(store); + + return { + injectSaga: injectSagaFactory(store, true), + ejectSaga: ejectSagaFactory(store, true), + }; +} diff --git a/front/odiparpack/app/utils/sagas.js b/front/odiparpack/app/utils/sagas.js new file mode 100644 index 0000000..f5d3e78 --- /dev/null +++ b/front/odiparpack/app/utils/sagas.js @@ -0,0 +1,15 @@ +import { all } from 'redux-saga/effects'; +import taskSagas from 'enl-containers/SampleFullstackApps//Todo/reducers/todoSagas'; +import contactSagas from 'enl-containers/SampleFullstackApps/Contact/reducers/contactSagas'; +import emailSagas from 'enl-containers/SampleFullstackApps/Email/reducers/emailSagas'; +import authSagas from 'enl-redux/modules/authSagas'; + + +export default function* sagas() { + yield all([ + ...authSagas, + ...contactSagas, + ...taskSagas, + ...emailSagas + ]); +} diff --git a/front/odiparpack/app/utils/tests/.DS_Store b/front/odiparpack/app/utils/tests/.DS_Store Binary files differnew file mode 100644 index 0000000..5008ddf --- /dev/null +++ b/front/odiparpack/app/utils/tests/.DS_Store |
