Skip to content

Commit

Permalink
Implement redux and add your user in redux store.
Browse files Browse the repository at this point in the history
  • Loading branch information
ahendouz committed Mar 9, 2019
1 parent e625f22 commit 2e305c1
Show file tree
Hide file tree
Showing 17 changed files with 256 additions and 18 deletions.
1 change: 0 additions & 1 deletion server/src/authentication/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ const signup = async (
const newProfile = await new Profile({ user: { _id: newUser.id } }).save();
// Return token.
return res.status(200).json({
seccess: true,
token: `Bearer ${createToken({ user: newUser })}`
});
}
Expand Down
4 changes: 4 additions & 0 deletions web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@
"dependencies": {
"axios": "^0.18.0",
"prop-types": "^15.7.2",
"jwt-decode": "^2.2.0",
"react": "^16.8.4",
"react-dom": "^16.8.4",
"react-redux": "^6.0.1",
"react-router-dom": "^4.3.1",
"react-scripts": "2.1.5",
"redux": "^4.0.1",
"redux-thunk": "^2.3.0",
"styled-components": "^4.1.3",
"styled-icons": "^7.4.2"
},
Expand Down
48 changes: 48 additions & 0 deletions web/src/actions/authActions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import axios from "axios";
import jwt_decode from "jwt-decode";

import { SET_CURRENT_USER } from "./types";
import { GET_ERRORS } from "./types";

import { setAuthToken } from "../utils/setAuthToken";

// Register user.
export const signupUserAction = (userType, newUser) => dispatch => {
axios
.post(`api/users/signup/${userType}`, newUser)
.then(res => {
test(res, dispatch);
})
.catch(err => {
dispatch({
type: GET_ERRORS,
payload: err.response.data
});
});
};

const test = (res, dispatch) => {
const { token } = res.data; // Save to localStorage.

localStorage.setItem("jwtToken", token); // Set Token to localStorage.

setAuthToken(token); // Set token to auth header.

const decoded = jwt_decode(token); // Decode token to user data.

dispatch(setCurrentUser(decoded)); // Set current user.
};

export const setCurrentUser = decoded => {
return {
type: SET_CURRENT_USER,
payload: decoded
};
};

// Logout a user.
export const logoutUser = () => dispatch => {
localStorage.removeItem("jwtToken"); // Remove token from local storage.
setAuthToken(false); // Remove auth header for future requests.
dispatch(setCurrentUser({})); // Set current user to {} which will set isAuthenticated to false.
};
2 changes: 2 additions & 0 deletions web/src/actions/types.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const GET_ERRORS = "GET_ERRORS";
export const SET_CURRENT_USER = "SET_CURRENT_USER";
7 changes: 7 additions & 0 deletions web/src/components/Dashboard/Dashboard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import React from "react";

const Dashboard = () => {
return <div>Hello Dashboard</div>;
};

export default Dashboard;
22 changes: 14 additions & 8 deletions web/src/components/auth/Signup.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import React, { Component } from "react";
import axios from "axios";
import { connect } from "react-redux";
import { signupUserAction } from "../../actions/authActions";

import { AuthDropStyle } from "../../styles";
import TextFieldGroup from "../common/TextFieldGroup";
Expand All @@ -25,19 +27,20 @@ class Signup extends Component {

handleSubmit = e => {
e.preventDefault();
const { userType } = this.props;
const { userType, signupUserAction } = this.props;
const { name, email, password } = this.state;
const newUser = {
name,
email,
password
};
axios
.post(`api/users/signup/${userType}`, newUser)
.then(res => console.log(res))
.catch(err => {
console.error(err);
});
signupUserAction(userType, newUser);
// axios
// .post(`api/users/signup/${userType}`, newUser)
// .then(res => console.log(res))
// .catch(err => {
// console.error(err);
// });
};

render() {
Expand Down Expand Up @@ -89,4 +92,7 @@ class Signup extends Component {
);
}
}
export default Signup;
export default connect(
null,
{ signupUserAction }
)(Signup);
19 changes: 12 additions & 7 deletions web/src/index.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import { ThemeProvider } from "styled-components";
import { Root } from "./routes";
import MyProvider from "./context/authTypeContext";
import { ThemeProvider } from "styled-components";
import { Provider } from "react-redux";
import { theme } from "./theme";
import "./index.css";
import { store } from "./store";
import withAuth from "./lib/withAuth";

ReactDOM.render(
<MyProvider>
<ThemeProvider theme={theme}>
<Root />
</ThemeProvider>
</MyProvider>,
<Provider store={store}>
<MyProvider>
<ThemeProvider theme={theme}>
<Root />
</ThemeProvider>
</MyProvider>
</Provider>,
document.getElementById("root")
);
27 changes: 27 additions & 0 deletions web/src/lib/PrivateRoute.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React from "react";
import { Route, Redirect } from "react-router-dom";
import { connect } from "react-redux";
import PropTypes from "prop-types";

const PrivateRoute = ({ component: Component, auth, ...rest }) => (
<Route
{...rest}
render={props =>
auth.isAuthenticated === true ? (
<Component {...props} />
) : (
<Redirect to="/login" />
)
}
/>
);

PrivateRoute.propTypes = {
auth: PropTypes.object.isRequired
};

const mapStateToProps = state => ({
auth: state.auth
});

export default connect(mapStateToProps)(PrivateRoute);
21 changes: 21 additions & 0 deletions web/src/lib/withAuth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { setAuthToken } from "../utils/setAuthToken";
import jwt_decode from "jwt-decode";

import { store } from "../store";
import { setCurrentUser, logoutUser } from "../actions/authActions";

const token = localStorage.jwtToken;
// Check for token
if (token) {
setAuthToken(token); // Set auth token to header auth.
const decoded = jwt_decode(token); // Decode token and get user info and exp.
store.dispatch(setCurrentUser(decoded)); // Set user and isAuthenticated

// Check for token expired token.
const currentTime = Date.now() / 1000;
if (decoded.exp < currentTime) {
store.dispatch(logoutUser()); // Logout user.
// TODO: clear current profile.
window.location.href = "signin"; // Redirect to signin
}
}
21 changes: 21 additions & 0 deletions web/src/reducers/authReducer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { SET_CURRENT_USER } from "../actions/types";
import { isEmpty } from "../validation/isEmpty";

const initialState = {
isAuthenticated: false,
user: {}
};

export default function(state = initialState, action) {
switch (action.type) {
case SET_CURRENT_USER:
return {
...state,
isAuthenticated: !isEmpty(action.payload),

user: action.payload
};
default:
return state;
}
}
15 changes: 15 additions & 0 deletions web/src/reducers/errorReducer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { GET_ERRORS } from "../actions/types";

const initialState = {
isAuthenticated: false,
user: {}
};

export default function(state = initialState, action) {
switch (action.type) {
case GET_ERRORS:
return action.payload;
default:
return state;
}
}
8 changes: 8 additions & 0 deletions web/src/reducers/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { combineReducers } from "redux";
import authReducer from "./authReducer.js";
import errorReducer from "./errorReducer";

export default combineReducers({
auth: authReducer,
errors: errorReducer
});
4 changes: 3 additions & 1 deletion web/src/routes/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
// Components.
import Navbar from "../components/layout/Navbar";
import Home from "../components/Home";
import Dashboard from "../components/Dashboard/Dashboard";

export const Root = () => (
<Router>
<Fragment>
<Navbar />
<Switch>
<Route component={Home} />
<Route path="/" exact component={Home} />
<Route path="/dashboard" exact component={Dashboard} />
</Switch>
</Fragment>
</Router>
Expand Down
16 changes: 16 additions & 0 deletions web/src/store/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { createStore, applyMiddleware, compose } from "redux";
import thunk from "redux-thunk";
import rootReducer from "../reducers";

const middleware = [thunk];

const initialState = {};

export const store = createStore(
rootReducer,
initialState,
compose(
applyMiddleware(...middleware),
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
)
);
10 changes: 10 additions & 0 deletions web/src/utils/setAuthToken.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import axios from "axios";

export const setAuthToken = token => {
if (token) {
// Apply to every request.
axios.defaults.headers.common["Authorization"] = token;
} else {
delete axios.defaults.headers.common["Authorization"];
}
};
5 changes: 5 additions & 0 deletions web/src/validation/isEmpty.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const isEmpty = value =>
value === undefined ||
value === null ||
(typeof value === "object" && Object.keys(value).length === 0) ||
(typeof value === "string" && value.trim().length === 0);
44 changes: 43 additions & 1 deletion web/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -727,6 +727,12 @@
dependencies:
regenerator-runtime "^0.12.0"

"@babel/runtime@^7.3.1":
version "7.3.4"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.3.4.tgz#73d12ba819e365fcf7fd152aed56d6df97d21c83"
dependencies:
regenerator-runtime "^0.12.0"

"@babel/template@^7.1.0", "@babel/template@^7.1.2", "@babel/template@^7.2.2":
version "7.2.2"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.2.2.tgz#005b3fdf0ed96e88041330379e0da9a708eb2907"
Expand Down Expand Up @@ -3888,6 +3894,12 @@ hoist-non-react-statics@^2.5.0:
version "2.5.5"
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz#c5903cf409c0dfd908f388e619d86b9c1174cb47"

hoist-non-react-statics@^3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.0.tgz#b09178f0122184fb95acf525daaecb4d8f45958b"
dependencies:
react-is "^16.7.0"

home-or-tmp@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8"
Expand Down Expand Up @@ -5027,6 +5039,10 @@ jsx-ast-utils@^2.0.1:
dependencies:
array-includes "^3.0.3"

jwt-decode@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/jwt-decode/-/jwt-decode-2.2.0.tgz#7d86bd56679f58ce6a84704a657dd392bba81a79"

killable@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892"
Expand Down Expand Up @@ -6963,10 +6979,21 @@ react-error-overlay@^5.1.4:
version "5.1.4"
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-5.1.4.tgz#88dfb88857c18ceb3b9f95076f850d7121776991"

react-is@^16.6.0, react-is@^16.8.1:
react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.2:
version "16.8.4"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.4.tgz#90f336a68c3a29a096a3d648ab80e87ec61482a2"

react-redux@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-6.0.1.tgz#0d423e2c1cb10ada87293d47e7de7c329623ba4d"
dependencies:
"@babel/runtime" "^7.3.1"
hoist-non-react-statics "^3.3.0"
invariant "^2.2.4"
loose-envify "^1.4.0"
prop-types "^15.7.2"
react-is "^16.8.2"

react-router-dom@^4.3.1:
version "4.3.1"
resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-4.3.1.tgz#4c2619fc24c4fa87c9fd18f4fb4a43fe63fbd5c6"
Expand Down Expand Up @@ -7123,6 +7150,17 @@ [email protected]:
dependencies:
minimatch "3.0.4"

redux-thunk@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.3.0.tgz#51c2c19a185ed5187aaa9a2d08b666d0d6467622"

redux@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.1.tgz#436cae6cc40fbe4727689d7c8fae44808f1bfef5"
dependencies:
loose-envify "^1.4.0"
symbol-observable "^1.2.0"

regenerate-unicode-properties@^8.0.1:
version "8.0.1"
resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.0.1.tgz#58a4a74e736380a7ab3c5f7e03f303a941b31289"
Expand Down Expand Up @@ -8026,6 +8064,10 @@ svgo@^1.0.0, svgo@^1.1.1:
unquote "~1.1.1"
util.promisify "~1.0.0"

symbol-observable@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804"

symbol-tree@^3.2.2:
version "3.2.2"
resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.2.tgz#ae27db38f660a7ae2e1c3b7d1bc290819b8519e6"
Expand Down

0 comments on commit 2e305c1

Please sign in to comment.