Explorar el Código

Added Redux state management

Fela Maslen hace 7 años
padre
commit
d80b0bc5fb

+ 39 - 0
package-lock.json

@@ -2942,6 +2942,11 @@
         "sha.js": "^2.4.8"
       }
     },
+    "create-reducer-object": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/create-reducer-object/-/create-reducer-object-1.0.0.tgz",
+      "integrity": "sha512-MUItFIRc+HucazizTpwCBiQCPjfYZIxCIJVnUEZWD6KS5G7boBBbQ4QFowKE+O3bOhBNu8vdFeJQrYFFglXl2A=="
+    },
     "cross-spawn": {
       "version": "6.0.5",
       "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
@@ -3184,6 +3189,12 @@
       "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=",
       "dev": true
     },
+    "deep-diff": {
+      "version": "0.3.8",
+      "resolved": "https://registry.npmjs.org/deep-diff/-/deep-diff-0.3.8.tgz",
+      "integrity": "sha1-wB3mPvsO7JeYgB1Ax+Da4ltYLIQ=",
+      "dev": true
+    },
     "deep-eql": {
       "version": "3.0.1",
       "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz",
@@ -9361,6 +9372,29 @@
         }
       }
     },
+    "redux": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/redux/-/redux-4.0.1.tgz",
+      "integrity": "sha512-R7bAtSkk7nY6O/OYMVR9RiBI+XghjF9rlbl5806HJbQph0LJVHZrU5oaO4q70eUKiqMRqm4y07KLTlMZ2BlVmg==",
+      "requires": {
+        "loose-envify": "^1.4.0",
+        "symbol-observable": "^1.2.0"
+      }
+    },
+    "redux-logger": {
+      "version": "3.0.6",
+      "resolved": "https://registry.npmjs.org/redux-logger/-/redux-logger-3.0.6.tgz",
+      "integrity": "sha1-91VZZvMJjzyIYExEnPC69XeCdL8=",
+      "dev": true,
+      "requires": {
+        "deep-diff": "^0.3.5"
+      }
+    },
+    "redux-saga": {
+      "version": "0.16.2",
+      "resolved": "https://registry.npmjs.org/redux-saga/-/redux-saga-0.16.2.tgz",
+      "integrity": "sha512-iIjKnRThI5sKPEASpUvySemjzwqwI13e3qP7oLub+FycCRDysLSAOwt958niZW6LhxfmS6Qm1BzbU70w/Koc4w=="
+    },
     "regenerate": {
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz",
@@ -10620,6 +10654,11 @@
         "has-flag": "^3.0.0"
       }
     },
+    "symbol-observable": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz",
+      "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ=="
+    },
     "symbol-tree": {
       "version": "3.2.2",
       "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.2.tgz",

+ 4 - 0
package.json

@@ -60,6 +60,7 @@
     "postcss-import": "^12.0.1",
     "postcss-loader": "^3.0.0",
     "react-hot-loader": "^4.6.3",
+    "redux-logger": "^3.0.6",
     "sass-loader": "^7.1.0",
     "style-loader": "^0.23.1",
     "webpack": "^4.28.4",
@@ -71,6 +72,7 @@
     "@babel/polyfill": "^7.2.5",
     "@slack/client": "^4.8.0",
     "body-parser": "^1.18.3",
+    "create-reducer-object": "^1.0.0",
     "express": "^4.16.4",
     "express-async-errors": "^3.1.1",
     "express-basic-auth": "^1.1.6",
@@ -81,6 +83,8 @@
     "react": "^16.7.0",
     "react-dom": "^16.7.0",
     "react-redux": "^6.0.0",
+    "redux": "^4.0.1",
+    "redux-saga": "^0.16.2",
     "request-promise": "^4.2.2",
     "soap": "^0.25.0",
     "winston": "^3.1.0"

+ 12 - 2
src/components/App/index.js

@@ -1,10 +1,20 @@
 import React from 'react';
+import PropTypes from 'prop-types';
+import { Provider } from 'react-redux';
 
 import './style.scss';
 
-export default function App() {
+export default function App({ store }) {
     return (
-        <h1>{'Hello world!'}</h1>
+        <div className="gurubot2">
+            <Provider store={store}>
+                <h1>{'GuruBot2 Admin Panel'}</h1>
+            </Provider>
+        </div>
     );
 }
 
+App.propTypes = {
+    store: PropTypes.object.isRequired
+};
+

+ 5 - 1
src/index.js

@@ -3,10 +3,14 @@ import '@babel/polyfill';
 import React from 'react';
 import { render } from 'react-dom';
 
+import { createStore } from './store';
+
 import App from 'components/App';
 
+const store = createStore();
+
 function renderApp(AppComponent = App) {
-    render(<AppComponent />, document.getElementById('root'));
+    render(<AppComponent store={store} />, document.getElementById('root'));
 }
 
 if (process.env.NODE_ENV !== 'test') {

+ 5 - 0
src/reducers/index.js

@@ -0,0 +1,5 @@
+import { createReducerObject } from 'create-reducer-object';
+
+export const rootReducer = createReducerObject({
+});
+

+ 4 - 0
src/sagas/index.js

@@ -0,0 +1,4 @@
+export function *rootSaga() {
+    yield 1;
+}
+

+ 80 - 0
src/store.js

@@ -0,0 +1,80 @@
+import { applyMiddleware, createStore } from 'redux';
+import createSagaMiddleware from 'redux-saga';
+import { createLogger } from 'redux-logger';
+
+import { rootReducer } from 'reducers';
+import { rootSaga } from 'sagas';
+
+const SKIP_LOG_ACTIONS = (process.env.SKIP_LOG_ACTIONS || '').split(',');
+
+export function getStoreMiddleware(__DEV__) {
+    const middleware = [rootReducer];
+
+    if (__DEV__) {
+        const devToolsMiddleware = window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__({
+            actionsBlacklist: SKIP_LOG_ACTIONS
+        });
+
+        middleware.push(devToolsMiddleware);
+    }
+
+    return middleware;
+}
+
+export function makeCreateStoreWithMiddleware(__DEV__, sagaMiddleware) {
+    const middleware = [sagaMiddleware];
+
+    if (__DEV__) {
+        const logger = createLogger({
+            collapsed: true,
+            predicate: (getState, action) => !SKIP_LOG_ACTIONS.includes(action.type)
+        });
+
+        middleware.push(logger);
+    }
+
+    return applyMiddleware(...middleware)(createStore);
+}
+
+function createCustomStore() {
+    const __DEV__ = process.env.NODE_ENV === 'development';
+    const __TEST__ = process.env.NODE_ENV === 'test';
+
+    if (__TEST__) {
+        return createStore(rootReducer);
+    }
+
+    const sagaMiddleware = createSagaMiddleware();
+
+    const createStoreWithMiddleware = makeCreateStoreWithMiddleware(__DEV__, sagaMiddleware);
+
+    const store = createStoreWithMiddleware(...getStoreMiddleware(__DEV__));
+
+    let sagaTask = sagaMiddleware.run(function *getSagas() {
+        yield rootSaga();
+    });
+
+    if (__DEV__ && module.hot) {
+        module.hot.accept('./reducers', () => {
+            // eslint-disable-next-line global-require
+            store.replaceReducer(require('./reducers').default);
+        });
+
+        module.hot.accept('./sagas', () => {
+            // eslint-disable-next-line global-require
+            const newRootSaga = require('./sagas').rootSaga;
+
+            sagaTask.cancel();
+            sagaTask.done.then(() => {
+                sagaTask = sagaMiddleware.run(function *replacedSaga() {
+                    yield newRootSaga();
+                });
+            });
+        });
+    }
+
+    return store;
+}
+
+export { createCustomStore as createStore };
+

+ 0 - 15
test/components/App/index.spec.js

@@ -1,15 +0,0 @@
-import '../../browser';
-import { shallow } from 'enzyme';
-import { expect } from 'chai';
-import React from 'react';
-import App from 'components/App';
-
-describe('<App />', () => {
-    const wrapper = shallow(<App />);
-
-    it('should render a hello world message', () => {
-        expect(wrapper.is('h1')).to.equal(true);
-        expect(wrapper.text()).to.equal('Hello world!');
-    });
-});
-

+ 1 - 0
webpack.config.js

@@ -157,6 +157,7 @@ module.exports = {
             constants: path.resolve(__dirname, 'src/constants'),
             containers: path.resolve(__dirname, 'src/containers'),
             reducers: path.resolve(__dirname, 'src/reducers'),
+            sagas: path.resolve(__dirname, 'src/sagas'),
             sprite: path.resolve(__dirname, 'src/images/sprite/sprite.scss')
         }
     },