问题
Trying to render a component only the first time user logs into my MERN app to show a questionnaire, otherwise user is taken to the dashboard. I am able to manipulate redux state to show firstLogin
as changing in my auth reducer by using an action that sets firstlogin to true (independent of the FirstLogin value changed in my database) after the user fills the questionnaire and even update the value of FirstLogin
in the User's collection in my mongodb.
I am sharing my authActions
authReducer
, users.js
and components Dashboard
and Assessment
.
~AuthActions~
import axios from "axios";
import setAuthToken from "../utils/setAuthToken";
import jwt_decode from "jwt-decode";
import { GET_ERRORS, SET_CURRENT_USER, USER_LOADING, UPDATE_USER } from "./types";
// Register User
export const registerUser = (userData, history) => dispatch => {
axios
.post("/api/users/register", userData)
.then(res => history.push("/login"))
// .then(res => {
// if(userData.FirstLogin){
// history.push("/login")
// }
// })
.catch(err =>
dispatch({
type: GET_ERRORS,
payload: err.response.data
})
);
};
//Save assessment data and set firstLogin to true
export const createAssessment = (newAssessment, history) => dispatch => {
axios
.post("/api/users/assessment", newAssessment)
.then(assessment => {
// in routes for assessment configure postedBy property(of user) to return true after assignment is submitted
const value = true
dispatch(updateUser(value))
history.push("/dashboard");
}).catch(err =>
dispatch({
type: GET_ERRORS,
payload: err.response.data
}))
// axios.get("api/users/dashboard", newAssessment.email).then()
};
// Login - get user token
export const loginUser = userData => dispatch => {
axios
.post("/api/users/login", userData)
.then(res => {
// Save to localStorage
// Set token to localStorage
const { token } = res.data;
localStorage.setItem("jwtToken", token);
// Set token to Auth header
setAuthToken(token);
// Decode token to get user data
const decoded = jwt_decode(token);
// Set current user
dispatch(setCurrentUser(decoded));
})
.catch(err =>
dispatch({
type: GET_ERRORS,
payload: err.response.data
})
);
};
export const updateUser = (value) => {
return{
type: UPDATE_USER,
payload: value
};
};
// Set logged in user
export const setCurrentUser = decoded => {
return {
type: SET_CURRENT_USER,
payload: decoded
};
};
// User loading
export const setUserLoading = () => {
return {
type: USER_LOADING
};
};
// Log user out
export const logoutUser = () => dispatch => {
// Remove token from local storage
localStorage.removeItem("jwtToken");
// Remove auth header for future requests
setAuthToken(false);
// Set current user to empty object {} which will set isAuthenticated to false
dispatch(setCurrentUser({}));
};
~authReducer~
import { SET_CURRENT_USER, UPDATE_USER, USER_LOADING } from "../actions/types";
const isEmpty = require("is-empty");
const initialState = {
isAuthenticated: false,
user: {},
loading: false,
firstLogin: false
};
export default function(state = initialState, action) {
switch (action.type) {
case SET_CURRENT_USER:
return {
...state,
isAuthenticated: !isEmpty(action.payload),
user: action.payload,
firstLogin: false
};
case USER_LOADING:
return {
...state,
loading: true
};
case UPDATE_USER:
return {
...state,
firstLogin: action.payload
};
default:
return state;
}
}
const express = require("express");
const router = express.Router();
const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
const multer = require('multer');
const mongoose = require("mongoose");
// const { v4: uuidV4 } = require('uuid');
const keys = require("../../config/keys");
const passport = require("passport");
var crypto = require('crypto');
// var nodemailer = require('nodemailer');
// var sgTransport = require('nodemailer-sendgrid-transport');
// const sendEmail = require('./email.send');
// const msgs = require('./email.msgs');
// const templates = require('./email.templates');
// Load input validation
const validateRegisterInput = require("../../validation/register");
const validateLoginInput = require("../../validation/login");
const { check, validationResult } = require('express-validator');
// Load User model and Token model
const User = require("../../models/User");
// const Token = require("../../models/Token")
const registeredemails = require("../../models/RegisteredEmails");
const Assessment = require("../../models/Assessment");
// @route POST api/users/register
// @desc Register user
// @access Public
router.post("/register", type, function (req, res, next) {
// var tmp_path = req.file.path;
if(!req.file){
console.log("File missing");
}
// Form validation
const { errors, isValid } = validateRegisterInput(req.body);
const url = req.protocol + '://' + req.get('host')
// Check validation
if (!isValid) {
return res.status(400).json(errors);
}
//Checks email against registered emails in database table
registeredemails.findOne({ email: req.body.email}).select("email").lean().then(result => {
if (!result) {
return res.status(400).json({email: "Email not provided"});
}
});
User.findOne({ email: req.body.email }).then(user =>
{
if (user) {return res.status(400).json({ email: "Email already exists" })
}
else {
const newUser = new User({
firstName: req.body.firstName,
lastName: req.body.lastName,
email: req.body.email,
password: req.body.password,
fileimg: url + '/public/' + req.file.filename
});
// // Hash password before saving in database
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(newUser.password, salt, (err, hash) => {
if (err) throw err;
newUser.password = hash;
newUser
.save().then(user => {res.json(user)})
.catch(err => console.log(err))
});
})
}
})
});
router.get("/dashboard", (req, res, next) => {
User.find({}).then(user => {
res.json(user);
})
})
router.post("/assessment", (req, res, next) => {
if(!req.body){
console.log("No assessment data recieved");
}else{
User.findOne({email: req.body.email}).then(user => {
user.firstLogin = true;
user.save();
Assessment.findOne({uAge: req.body.uAge}).then(assessment =>
{
if (assessment) {return res.status(400).json({ assessment: "Assessment already exists" })
}
else {
var assessment = new Assessment({
uAge: req.body.uAge,
uCountry : req.body.uCountry
});
assessment.save(function (err, data) {
if (err) {
console.log(err);
res.send(err.message);
return;
}else{
res.send('success');
return;
}
});
}
});
})}
});
// @route POST api/users/login
// @desc Login user and return JWT token
// @access Public
router.post("/login", (req, res) => {
// Form validation
const { errors, isValid } = validateLoginInput(req.body);
// Check validation
if (!isValid) {
return res.status(400).json(errors);
}
const email = req.body.email;
const password = req.body.password;
// Find user by email
User.findOne({ email }).then(user => {
// Check if user exists
if (!user) {
return res.status(404).json({ emailnotfound: "Email not found" });
}
// Make sure the user has been verified
// if (!user.confirmed) return res.status(401).send({ type: 'not-verified', msg: 'Your account has not been verified.' });
// Check password
bcrypt.compare(password, user.password).then(isMatch => {
if (isMatch) {
// User matched
// Create JWT Payload
const payload = {
id: user.id,
name: user.firstName
};
// Sign token
jwt.sign(
payload,
keys.secretOrKey,
{
expiresIn: 31556926 // 1 year in seconds
},
(err, token) => {
res.json({
success: true,
token: "Bearer " + token
});
}
);
} else {
return res
.status(400)
.json({ passwordincorrect: "Password incorrect" });
}
});
});
});
module.exports = router;
~Assessment~
import React, { Component } from "react";
import axios from "axios";
import { Link, withRouter } from "react-router-dom";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import { createAssessment } from "../../actions/authActions";
import classnames from "classnames";
import { Container, Row, Col } from 'reactstrap';
// import { Button, Form, FormGroup, Label, Input, FormText } from 'reactstrap';
import UserNavbar from "../dashboard/UserNavbar.js";
// import { notify } from 'react-notify-toast'
// import { API_URL } from './../../config'
import { Button } from '@material-ui/core';
// import './register.css';
class AssessmentSimple extends Component {
constructor() {
super();
this.state = {
uAge: "",
email: "",
// uGender: '',
uCountry: ""
};
}
componentDidMount() {
// If logged in and user navigates to Login page, should redirect them to dashboard
if (this.props.auth.firstLogin) {
this.props.history.push("/dashboard");
}
}
onChange = e => {
this.setState({ [e.target.id]: e.target.value });
};
onSubmit = e => {
e.preventDefault();
// const { email, username, password } = this.state
// const { user } = this.props.auth;
const newAssessment = {
uAge: this.state.uAge,
uCountry: this.state.uCountry,
email: this.state.email
};
// axios({
// url: '/api/users/dashboard',
// method: 'POST',
// data: user.email
// }).then
// axios.post("/api/users/assessment", newAssessment)
// .then(this.props.history.push("/dashboard")).catch(err =>
// console.log(err))
this.props.createAssessment(newAssessment, this.props.history)
}
render() {
// const { errors } = this.state;
const { user } = this.props.auth;
return (
<div>
<UserNavbar />
<Container>
<Row>
<Col sm="12" md={{ size: 6, offset: 3 }}>
<form onSubmit={this.onSubmit}>
<h4 style={{ fontFamily: "Montserrat", "font-size": "40px", "margin-top": "50px"}}>
<b>Welcome to Healthynox</b>
</h4>
<p style={{ fontFamily: "Montserrat", "color":"grey", "font-size": "17px", "margin-top":"15px"}}>
Start your mental health journey and register here.
</p>
<input
onChange={this.onChange}
// name = 'uAge'
// ref={input => this.uAge = input}
value={this.state.uAge}
// error={errors.uAge}
id="uAge"
placeholder="uAge (Provided by your workplace)"
style={{ fontFamily: "Montserrat", "color": "grey", "font-size": "17px", "background-color": "white", "border-radius": "25px", "width": "400px","border": "2rm",
"border-style": "solid", "border-color": "black", "display": "inline-block", "padding": "6px 12px"}}
type="text"
/>
{/* <span className="red-text">
{errors.uAge}
{errors.uAgenotfound}
</span> */}
<p style={{ fontFamily: "Montserrat", "color": "grey", "font-size": "17px", "margin-top": "25px"}}>Please choose a password (6 or more characters)</p>
<input
onChange={this.onChange}
value={this.state.uCountry}
// error={errors.password}
id="uCountry"
placeholder="uCountry"
style={{ fontFamily: "Montserrat", "color": "grey", "font-size": "17px", "background-color": "white", "border-radius": "25px", "width": "400px","border": "2rm",
"border-style": "solid", "border-color": "black", "display": "inline-block", "padding": "6px 12px"}}
type="text"
// className={classnames("", {
// invalid: errors.password
// })}
/>
<input
onChange={this.onChange}
value={this.state.email}
id="email"
placeholder="email (Provided by your workplace)"
style={{ fontFamily: "Montserrat", "color": "grey", "font-size": "17px", "background-color": "white", "border-radius": "25px", "width": "400px","border": "2rm",
"border-style": "solid", "border-color": "black", "display": "inline-block", "padding": "6px 12px"}}
type="text"
/>
{/* <span className="red-text">{errors.password}</span> */}
<div>
<Button style={{
"display": "inline-block",
"width": "150px",
"borderRadius": "40px",
"letterSpacing": "1px",
"marginTop": "1rem",
"backgroundColor": "blue",
"fontFamily": "Montserrat", "color": "white", "font-size": "17px"
}} type="submit">
Submit
</Button>
</div>
</form>
</Col>
</Row>
</Container>
</div>
);
}
}
AssessmentSimple.propTypes = {
createAssessment: PropTypes.func.isRequired,
auth: PropTypes.object.isRequired
// errors: PropTypes.object.isRequired
};
const mapStateToProps = state => ({
createAssessment: state.createAssessment,
auth: state.auth
// errors: state.errors
});
export default connect(
mapStateToProps,
{ createAssessment }
)(withRouter(AssessmentSimple));
~Dashboard~
import React, { Component } from "react";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import axios from 'axios';
import { logoutUser } from "../../actions/authActions";
import './Dashboard.css';
import './UserNavbar.js'
import UserNavbar from "./UserNavbar.js";
import { Container, Row, Button, Col } from 'reactstrap';
import {
Card, CardImg, CardText, CardBody,
CardTitle, CardSubtitle
} from 'reactstrap';
// import { Button, ButtonToolbar, Modal } from 'react-bootstrap';
import "bootstrap/dist/css/bootstrap.min.css";
import Advance from './Advance.png';
import Assessments from './Assessments.png';
import Mood from './Mood.png';
import Sessions from './Sessions.png'
import Measure from './Measure.png'
import { Redirect } from "react-router-dom";
import Axios from "axios";
class Dashboard extends Component {
constructor() {
super();
this.state = {
firstLogin: ""
};
}
onLogoutClick = e => {
e.preventDefault();
this.props.logoutUser();
};
componentDidMount() {
// axios.get("http://localhost:5000/api/users/dashboard")
// .then(res => {
// const cond = res.data[15].firstLogin;
// this.setState({firstLogin: cond });
// });
// const firstLogin = this.props.auth.firstLogin;
if (this.props.auth.firstLogin) {
this.props.history.push("/dashboard");
}else{
this.props.history.push("/assessment");
}
}
render() {
const { user } = this.props.auth;
// const data = this.props;
return (
<div>
<UserNavbar/>
<Container>
<Row>
<Col xs="6" sm="4">
<h4 style={{"marginTop": "80px"}}>
<b style={{fontSize: "30px", fontFamily: "Montserrat"}}>Hey {user.name}!</b>
</h4>
<p className="flow-text grey-text text-darken-1" style={{fontFamily: "Montserrat"}}>
Weclome to Healthynox{" "}
</p>
<Button
style={{
width: "300px",
borderRadius: "3px",
letterSpacing: "1.5px",
marginTop: "1rem",
fontFamily: "Montserrat"
}}
onClick={this.onSubmitAssessment}
className="btn btn-large waves-effect waves-light hoverable blue accent-3"
>
Launch Short Self-Assessment
</Button>
</Col>
</Row>
<Row>
<Col xs="6" sm="4">
<Card>
<CardBody>
<CardTitle>Identify</CardTitle>
<CardText>Complete assessments regularly to learn more about your state and personality.</CardText>
<Button color= "primary">Take Assessment</Button>
</CardBody>
<CardImg top height= "100px"top width="100%" src={Assessments} alt="Card image cap" />
</Card>
</Col>
<Col xs="6" sm="4">
<Card>
<CardBody>
<CardTitle>Measure</CardTitle>
<CardText>Monitor and reflect on progress against agreed milestones.</CardText>
<Button color="primary">View Progress</Button>
</CardBody>
<CardImg top height= "100px" top width="100%" src={Measure} alt="Card image cap" />
</Card>
</Col>
<Col xs="6" sm="4">
<Card>
<CardBody>
<CardTitle>Advance</CardTitle>
<CardText>Work towards your goals by completing tasks set by your personal therapist.</CardText>
<Button color="primary">Complete Exercise</Button>
</CardBody>
<CardImg top height= "100px" top width="100%" src={Advance} alt="Card image cap" />
</Card>
</Col>
</Row>
<Row>
<Col xs="6">
<Card>
<CardBody>
<CardTitle>Upcoming Sessions</CardTitle>
<CardSubtitle>Introductory Session (50 Minutes) on the 30th of April 2020 at 1:00 PM with Sven and Barbara.
Join Session</CardSubtitle>
<CardImg top height="100px" top width="100%" src={Sessions} alt="Card image cap" />
<Button color="primary">Schedule Session</Button>
</CardBody>
</Card>
</Col>
<Col xs="6">
<Card>
<CardBody>
<CardTitle>Mood Barometer</CardTitle>
<CardSubtitle>Today I Feel</CardSubtitle>
<CardImg top height = "100px" top width="20px" src={Mood} alt="Card image cap" />
<Button color="primary">Send</Button>
</CardBody>
</Card>
</Col>
</Row>
</Container>
</div>
);
}
}
Dashboard.propTypes = {
logoutUser: PropTypes.func.isRequired,
auth: PropTypes.object.isRequired
};
const mapStateToProps = state => ({
auth: state.auth
});
export default connect(
mapStateToProps,
{ logoutUser }
)(Dashboard);
来源:https://stackoverflow.com/questions/62365471/how-to-fetch-data-in-react-redux-from-mongoose-mongodb-using-router-to-reflect-c