summaryrefslogtreecommitdiffstats
path: root/front/odiparpack/app/utils
diff options
context:
space:
mode:
Diffstat (limited to 'front/odiparpack/app/utils')
-rw-r--r--front/odiparpack/app/utils/checkStore.js21
-rw-r--r--front/odiparpack/app/utils/constants.js3
-rw-r--r--front/odiparpack/app/utils/history.js3
-rw-r--r--front/odiparpack/app/utils/injectReducer.js45
-rw-r--r--front/odiparpack/app/utils/injectSaga.js59
-rw-r--r--front/odiparpack/app/utils/reducerInjectors.js33
-rw-r--r--front/odiparpack/app/utils/sagaInjectors.js92
-rw-r--r--front/odiparpack/app/utils/sagas.js15
-rw-r--r--front/odiparpack/app/utils/tests/.DS_Storebin0 -> 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
new file mode 100644
index 0000000..5008ddf
--- /dev/null
+++ b/front/odiparpack/app/utils/tests/.DS_Store
Binary files differ