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.
|
||||
disableDotRule: true,
|
||||
},
|
||||
historyApiFallback: true,
|
||||
public: allowedHost,
|
||||
proxy,
|
||||
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",
|
||||
"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": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz",
|
||||
@@ -10089,6 +10094,19 @@
|
||||
"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": {
|
||||
"version": "4.2.0",
|
||||
"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": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.3.3.tgz",
|
||||
@@ -11513,6 +11542,11 @@
|
||||
"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": {
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.2.tgz",
|
||||
|
||||
@@ -40,8 +40,10 @@
|
||||
"react": "^16.3.1",
|
||||
"react-dev-utils": "^5.0.1",
|
||||
"react-dom": "^16.3.1",
|
||||
"react-redux": "^5.0.7",
|
||||
"react-router-dom": "^4.2.2",
|
||||
"react-test-renderer": "^16.3.1",
|
||||
"redux": "^3.7.2",
|
||||
"resolve": "1.6.0",
|
||||
"sass-loader": "^6.0.7",
|
||||
"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 {MemoryRouter as Router} from 'react-router-dom'
|
||||
import Dashboard from './index';
|
||||
import {mount} from 'enzyme'
|
||||
import renderer from 'react-test-renderer';
|
||||
|
||||
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();
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
@@ -1,9 +1,16 @@
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux'
|
||||
import styles from './styles.scss';
|
||||
import Header from '../Header/index'
|
||||
|
||||
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 {
|
||||
padding-top : 12px;
|
||||
padding-bottom : 12px;
|
||||
padding-top : 6px;
|
||||
padding-bottom : 6px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items:center;
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
import React from 'react';
|
||||
import {connect} from 'react-redux'
|
||||
import styles from './styles.scss';
|
||||
import Header from '../Header/index'
|
||||
|
||||
const Inbox = (props) => {
|
||||
return <div className={styles.colored}><Header/>Inbox</div>;
|
||||
const Inbox = (props) => {
|
||||
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 { connect } from 'react-redux'
|
||||
import styles from './styles.scss';
|
||||
import Header from '../Header/index'
|
||||
|
||||
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 styles from './styles.scss';
|
||||
import Header from './Header';
|
||||
|
||||
class App extends Component {
|
||||
render() {
|
||||
return (
|
||||
<div className={styles.App}>
|
||||
<header className={styles["App-header"]}>
|
||||
<h1 className={styles["App-title"]}><Header name="Header"/> </h1>
|
||||
</header>
|
||||
</div>
|
||||
<Header />
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,15 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { BrowserRouter as Router, Route } from 'react-router-dom'
|
||||
import { createStore } from 'redux'
|
||||
import './index.css';
|
||||
import App from './components/App';
|
||||
import registerServiceWorker from './registerServiceWorker';
|
||||
import Inbox from './components/App/Inbox/index';
|
||||
import MyTask from './components/App/Mytask/index';
|
||||
import Dashboard from './components/App/Dashboard/index';
|
||||
import Dummy from './components/App/Dummy/index';
|
||||
|
||||
import subtaskIo from './reducers/index'
|
||||
import Root from './components/App/Root/index';
|
||||
|
||||
const store = createStore(subtaskIo)
|
||||
|
||||
ReactDOM.render(
|
||||
<Router>
|
||||
<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>
|
||||
,
|
||||
<Root store={store}/>,
|
||||
document.getElementById('root'));
|
||||
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