Added redux structure that remebers state
This commit is contained in:
@@ -79,6 +79,7 @@ module.exports = function(proxy, allowedHost) {
|
|||||||
// See https://github.com/facebookincubator/create-react-app/issues/387.
|
// See https://github.com/facebookincubator/create-react-app/issues/387.
|
||||||
disableDotRule: true,
|
disableDotRule: true,
|
||||||
},
|
},
|
||||||
|
historyApiFallback: true,
|
||||||
public: allowedHost,
|
public: allowedHost,
|
||||||
proxy,
|
proxy,
|
||||||
before(app) {
|
before(app) {
|
||||||
|
|||||||
34
frontend/package-lock.json
generated
34
frontend/package-lock.json
generated
@@ -7272,6 +7272,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.5.tgz",
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.5.tgz",
|
||||||
"integrity": "sha512-svL3uiZf1RwhH+cWrfZn3A4+U58wbP0tGVTLQPbjplZxZ8ROD9VLuNgsRniTlLe7OlSqR79RUehXgpBW/s0IQw=="
|
"integrity": "sha512-svL3uiZf1RwhH+cWrfZn3A4+U58wbP0tGVTLQPbjplZxZ8ROD9VLuNgsRniTlLe7OlSqR79RUehXgpBW/s0IQw=="
|
||||||
},
|
},
|
||||||
|
"lodash-es": {
|
||||||
|
"version": "4.17.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.8.tgz",
|
||||||
|
"integrity": "sha512-I9mjAxengFAleSThFhhAhvba6fsO0hunb9/0sQ6qQihSZsJRBofv2rYH58WXaOb/O++eUmYpCLywSQ22GfU+sA=="
|
||||||
|
},
|
||||||
"lodash._reinterpolate": {
|
"lodash._reinterpolate": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz",
|
||||||
@@ -10089,6 +10094,19 @@
|
|||||||
"prop-types": "15.6.1"
|
"prop-types": "15.6.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"react-redux": {
|
||||||
|
"version": "5.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-5.0.7.tgz",
|
||||||
|
"integrity": "sha512-5VI8EV5hdgNgyjfmWzBbdrqUkrVRKlyTKk1sGH3jzM2M2Mhj/seQgPXaz6gVAj2lz/nz688AdTqMO18Lr24Zhg==",
|
||||||
|
"requires": {
|
||||||
|
"hoist-non-react-statics": "2.5.0",
|
||||||
|
"invariant": "2.2.4",
|
||||||
|
"lodash": "4.17.5",
|
||||||
|
"lodash-es": "4.17.8",
|
||||||
|
"loose-envify": "1.3.1",
|
||||||
|
"prop-types": "15.6.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"react-router": {
|
"react-router": {
|
||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-4.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-router/-/react-router-4.2.0.tgz",
|
||||||
@@ -10257,6 +10275,17 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"redux": {
|
||||||
|
"version": "3.7.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/redux/-/redux-3.7.2.tgz",
|
||||||
|
"integrity": "sha512-pNqnf9q1hI5HHZRBkj3bAngGZW/JMCmexDlOxw4XagXY2o1327nHH54LoTjiPJ0gizoqPDRqWyX/00g0hD6w+A==",
|
||||||
|
"requires": {
|
||||||
|
"lodash": "4.17.5",
|
||||||
|
"lodash-es": "4.17.8",
|
||||||
|
"loose-envify": "1.3.1",
|
||||||
|
"symbol-observable": "1.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"regenerate": {
|
"regenerate": {
|
||||||
"version": "1.3.3",
|
"version": "1.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.3.3.tgz",
|
||||||
@@ -11513,6 +11542,11 @@
|
|||||||
"serviceworker-cache-polyfill": "4.0.0"
|
"serviceworker-cache-polyfill": "4.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": {
|
"symbol-tree": {
|
||||||
"version": "3.2.2",
|
"version": "3.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.2.tgz",
|
||||||
|
|||||||
@@ -40,8 +40,10 @@
|
|||||||
"react": "^16.3.1",
|
"react": "^16.3.1",
|
||||||
"react-dev-utils": "^5.0.1",
|
"react-dev-utils": "^5.0.1",
|
||||||
"react-dom": "^16.3.1",
|
"react-dom": "^16.3.1",
|
||||||
|
"react-redux": "^5.0.7",
|
||||||
"react-router-dom": "^4.2.2",
|
"react-router-dom": "^4.2.2",
|
||||||
"react-test-renderer": "^16.3.1",
|
"react-test-renderer": "^16.3.1",
|
||||||
|
"redux": "^3.7.2",
|
||||||
"resolve": "1.6.0",
|
"resolve": "1.6.0",
|
||||||
"sass-loader": "^6.0.7",
|
"sass-loader": "^6.0.7",
|
||||||
"style-loader": "0.19.0",
|
"style-loader": "0.19.0",
|
||||||
|
|||||||
6
frontend/src/actions/index.js
Normal file
6
frontend/src/actions/index.js
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
export const INIT_PAGE = 'INIT_PAGE'
|
||||||
|
|
||||||
|
export const initPage = titles => ({
|
||||||
|
type: INIT_PAGE,
|
||||||
|
titles
|
||||||
|
})
|
||||||
@@ -1,12 +1,20 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {MemoryRouter as Router} from 'react-router-dom'
|
import {MemoryRouter as Router} from 'react-router-dom'
|
||||||
import Dashboard from './index';
|
import Dashboard from './index';
|
||||||
|
import {mount} from 'enzyme'
|
||||||
import renderer from 'react-test-renderer';
|
import renderer from 'react-test-renderer';
|
||||||
|
|
||||||
test('Dummy renders correctly', () => {
|
test('Dummy renders correctly', () => {
|
||||||
const component = renderer.create(
|
|
||||||
<Router><Dashboard/></Router>,
|
const props = {
|
||||||
);
|
title: 'Dashboard',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// // const component = renderer.create(
|
||||||
|
// <Router><Dashboard props ={...props}/></Router>,
|
||||||
|
// );
|
||||||
|
const component = mount((<Router><Dashboard {...props} /></Router>))
|
||||||
let tree = component.toJSON();
|
let tree = component.toJSON();
|
||||||
expect(tree).toMatchSnapshot();
|
expect(tree).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
@@ -1,9 +1,16 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { connect } from 'react-redux'
|
||||||
import styles from './styles.scss';
|
import styles from './styles.scss';
|
||||||
import Header from '../Header/index'
|
import Header from '../Header/index'
|
||||||
|
|
||||||
const Dashboard = (props) => {
|
const Dashboard = (props) => {
|
||||||
return <div className={styles.colored}><Header/>Dashboard</div>;
|
return <div className={styles.colored}><Header />Title {props.title}</div>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Dashboard;
|
const mapStateToProps = (state) => {
|
||||||
|
return {
|
||||||
|
title: state.pageInitialised.titles.title,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(mapStateToProps)(Dashboard);
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`Dummy renders correctly 1`] = `
|
|
||||||
<div
|
|
||||||
className={undefined}
|
|
||||||
>
|
|
||||||
Hello,
|
|
||||||
Hamo
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import Dummy from './index';
|
|
||||||
import renderer from 'react-test-renderer';
|
|
||||||
|
|
||||||
test('Dummy renders correctly', () => {
|
|
||||||
const component = renderer.create(
|
|
||||||
<Dummy name="Hamo"/>,
|
|
||||||
);
|
|
||||||
let tree = component.toJSON();
|
|
||||||
expect(tree).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import styles from './styles.scss';
|
|
||||||
|
|
||||||
const Dummy = (props) => {
|
|
||||||
return <div className={styles.colored}>Hello, {props.name}</div>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Dummy;
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
.colored {
|
|
||||||
color: blueviolet;
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
.Header {
|
.Header {
|
||||||
padding-top : 12px;
|
padding-top : 6px;
|
||||||
padding-bottom : 12px;
|
padding-bottom : 6px;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items:center;
|
align-items:center;
|
||||||
|
|||||||
@@ -1,9 +1,16 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import {connect} from 'react-redux'
|
||||||
import styles from './styles.scss';
|
import styles from './styles.scss';
|
||||||
import Header from '../Header/index'
|
import Header from '../Header/index'
|
||||||
|
|
||||||
const Inbox = (props) => {
|
const Inbox = (props) => {
|
||||||
return <div className={styles.colored}><Header/>Inbox</div>;
|
return <div className={styles.colored}><Header/>Title {props.title}</div>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Inbox;
|
const mapStateToProps = (state) => {
|
||||||
|
return {
|
||||||
|
title: state.pageInitialised.titles.title,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(mapStateToProps)(Inbox);
|
||||||
@@ -1,9 +1,16 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { connect } from 'react-redux'
|
||||||
import styles from './styles.scss';
|
import styles from './styles.scss';
|
||||||
import Header from '../Header/index'
|
import Header from '../Header/index'
|
||||||
|
|
||||||
const MyTask = (props) => {
|
const MyTask = (props) => {
|
||||||
return <div className={styles.colored}><Header/>My Task</div>;
|
return <div className={styles.colored}><Header />Title {props.title}</div>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default MyTask;
|
const mapStateToProps = (state) => {
|
||||||
|
return {
|
||||||
|
title: state.pageInitialised.titles.title,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(mapStateToProps)(MyTask);
|
||||||
53
frontend/src/components/App/Root/index.js
Normal file
53
frontend/src/components/App/Root/index.js
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import React, { Component } from 'react'
|
||||||
|
import { Provider } from 'react-redux'
|
||||||
|
import { BrowserRouter as Router, Route } from 'react-router-dom'
|
||||||
|
|
||||||
|
import App from '../index'
|
||||||
|
import Inbox from '../Inbox/index';
|
||||||
|
import MyTask from '../Mytask/index';
|
||||||
|
import Dashboard from '../Dashboard/index';
|
||||||
|
import { initPage } from '../../../actions/index'
|
||||||
|
|
||||||
|
class Root extends Component {
|
||||||
|
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<Provider store={this.props.store}>
|
||||||
|
<Router>
|
||||||
|
<div>
|
||||||
|
<Route path='/' exact component={App} />
|
||||||
|
<Route path="/mytask" exact component={mountComponentWithPathAction(MyTask, this.props)} />
|
||||||
|
<Route path="/inbox" exact component={mountComponentWithPathAction(Inbox, this.props)} />
|
||||||
|
<Route path="/dashboard" exact component={mountComponentWithPathAction(Dashboard, this.props)} />
|
||||||
|
</div>
|
||||||
|
</Router>
|
||||||
|
</Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pass this function to `react-router-dom` Route component property
|
||||||
|
* In orderd to triger redux action that is related to te Route path
|
||||||
|
* If such action exists
|
||||||
|
*
|
||||||
|
* @param {} WrappedComponent a React component to render
|
||||||
|
* @param {*} props a an object containing redux `store` to dispach initPage action with route path name
|
||||||
|
* @returns A new HIGHT ORDER React component from WrappedComponent after redux action is dispatched
|
||||||
|
*/
|
||||||
|
let mountComponentWithPathAction = function (WrappedComponent, props) {
|
||||||
|
return class extends Component {
|
||||||
|
|
||||||
|
componentWillMount() {
|
||||||
|
props.store.dispatch(initPage({ pathName: this.props.location.pathname }))
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return <WrappedComponent {...this.props} />
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Root
|
||||||
@@ -1,15 +1,10 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import styles from './styles.scss';
|
|
||||||
import Header from './Header';
|
import Header from './Header';
|
||||||
|
|
||||||
class App extends Component {
|
class App extends Component {
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div className={styles.App}>
|
<Header />
|
||||||
<header className={styles["App-header"]}>
|
|
||||||
<h1 className={styles["App-title"]}><Header name="Header"/> </h1>
|
|
||||||
</header>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +1,15 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import { BrowserRouter as Router, Route } from 'react-router-dom'
|
import { createStore } from 'redux'
|
||||||
import './index.css';
|
import './index.css';
|
||||||
import App from './components/App';
|
|
||||||
import registerServiceWorker from './registerServiceWorker';
|
import registerServiceWorker from './registerServiceWorker';
|
||||||
import Inbox from './components/App/Inbox/index';
|
|
||||||
import MyTask from './components/App/Mytask/index';
|
import subtaskIo from './reducers/index'
|
||||||
import Dashboard from './components/App/Dashboard/index';
|
import Root from './components/App/Root/index';
|
||||||
import Dummy from './components/App/Dummy/index';
|
|
||||||
|
const store = createStore(subtaskIo)
|
||||||
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<Router>
|
<Root store={store}/>,
|
||||||
<div>
|
|
||||||
<Route exact path='/' component={App} />
|
|
||||||
<Route exact path="/mytask" component={MyTask} />
|
|
||||||
<Route exact path="/inbox" component={Inbox} />
|
|
||||||
<Route exact path="/dashboard" component={Dashboard} />
|
|
||||||
</div>
|
|
||||||
</Router>
|
|
||||||
,
|
|
||||||
document.getElementById('root'));
|
document.getElementById('root'));
|
||||||
registerServiceWorker();
|
registerServiceWorker();
|
||||||
|
|||||||
7
frontend/src/reducers/index.js
Normal file
7
frontend/src/reducers/index.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { combineReducers } from 'redux'
|
||||||
|
import pageInitialised from './router'
|
||||||
|
|
||||||
|
const subtaskIo = combineReducers({
|
||||||
|
pageInitialised,
|
||||||
|
})
|
||||||
|
export default subtaskIo
|
||||||
14
frontend/src/reducers/router.js
Normal file
14
frontend/src/reducers/router.js
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
const pageInitialised = (state = {
|
||||||
|
titles: { title: 'Default title' },
|
||||||
|
}, action) => {
|
||||||
|
switch (action.type) {
|
||||||
|
case 'INIT_PAGE':
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
titles: { title: action.titles.pathName }
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default pageInitialised
|
||||||
Reference in New Issue
Block a user