Trouble updating boolean value with save() using mongoose - javascript

So I've encountered an interesting issue where I've been able to use my front end to change a boolean value to true, but not back to false. My code:
updateUser.js (backend)
import express from 'express';
import mongoose from 'mongoose';
import User from '../models/userModel.js';
const app = express();
const uUserRouter = express.Router();
uUserRouter.post('/:id', (req, res) => {
User.findById(req.params.id)
.then(user => {
user.username = req.body.username || user.username;
user.email = req.body.email || user.email;
user.password = req.body.password || user.password;
user.avatar = req.body.avatar || user.avatar;
user.isBanned = req.body.isBanned || user.isBanned;
user.rank = req.body.rank || user.rank;
user.save()
.then(() => res.send('User Updated!'))
.catch(err => res.status(400).send("Update Failed: " + err.message))
})
.catch(err => res.status(404).send('This user cannot be found: ' + err.message))
});
export default uUserRouter;
adminPanel.component.js (frontend)
import axios from 'axios';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Button, Container, Table } from 'react-bootstrap';
import { Link } from 'react-router-dom';
const Topic = props => (
<tr>
<td className="tTitle">
{props.topic.title}
</td>
<td className="tDescription">
{props.topic.description}
</td>
<td><Button variant="secondary" onClick={() => { props.deleteTopic(props.topic._id) }}>Delete</Button></td>
</tr>
)
const User = props => (
<tr>
<td className="aPAvatar"><img src={props.user.avatar} height="15%" width="15%" alt="avatar"/></td>
<td>{props.user.username}</td>
<td>{props.user.rank}</td>
<td>{props.user.isBanned ? <Button variant="primary" onClick={() => { props.unBanUser(props.user._id) }}>Unban User</Button> : <Button variant="warning" onClick={() => { props.banUser(props.user._id) }}>Ban User</Button>}</td>
<td><Button variant="secondary" onClick={() => { props.deleteUser(props.user._id) }}>Delete</Button></td>
</tr>
)
class AdminPanel extends Component {
constructor(props) {
super(props);
this.banUser = this.banUser.bind(this);
this.deleteTopic = this.deleteTopic.bind(this);
this.deleteUser = this.deleteUser.bind(this);
this.unBanUser = this.unBanUser.bind(this);
this.state = {
topics: [],
users: []
};
}
componentDidMount() {
function getTopics() {
return axios.get('http://localhost:5000/forum/topics')
}
function getUsers() {
return axios.get('http://localhost:5000/users')
}
Promise.all([getTopics(), getUsers()])
.then((results) => {
const topics = results[0].data;
const users = results[1].data;
this.setState({topics, users}, () => {
console.log(this.state);
});
}).catch((e) => {
console.log('Error: ', e);
});
}
listTopics = () => {
return this.state.topics.map(currenttopic => {
return <Topic topic={currenttopic} deleteTopic={this.deleteTopic} key={currenttopic._id} />
})
}
listUsers = () => {
return this.state.users.map(currentuser => {
return <User user={currentuser} deleteUser={this.deleteUser} banUser={this.banUser} unBanUser={this.unBanUser} key={currentuser._id} />
})
}
banUser(id) {
if (window.confirm('Are you sure you want to ban this user?')) {
axios.post('http://localhost:5000/users/update/'+id, {isBanned: true})
.then(res => {console.log(res.data)})
.catch((e) => {
console.log('Error: ', e)
});
window.alert('User banned successfully');
window.location.reload();
} else {
window.alert('Ban Canceled');
}
}
unBanUser(id) {
axios.post('http://localhost:5000/users/update/'+id, {isBanned: false})
.then(res => {console.log(res.data)});
console.log('test');
window.alert('User Unbanned');
}
deleteTopic(id) {
if (window.confirm('Are you sure you want to delete this topic?')) {
axios.delete('http://localhost:5000/forum/topics/'+id)
.then(res => console.log(res.data));
this.setState({
topics: this.state.topics.filter(el => el._id !== id)
})
window.alert('Topic Deleted');
} else {
window.alert('Deletion Canceled');
}
}
deleteUser(id) {
if (window.confirm('Are you sure you want to delete this user?')) {
axios.delete('http://localhost:5000/users/delete/'+id)
.then(res => console.log(res.data));
this.setState({
users: this.state.users.filter(el => el._id !== id)
})
window.alert('User Deleted')
} else {
window.alert('Deletion Canceled')
}
}
render() {
return (
<Container>
{this.props.isLoggedIn && this.props.rank === 'Admin' ?
<Container>
<div className="userList">
<h3>Users</h3>
<Table>
<thead>
<tr>
<td>Avatar</td>
<td>Username</td>
<td>Rank</td>
<td>Ban Hammer</td>
<td>Delete</td>
</tr>
</thead>
<tbody>
{this.listUsers()}
</tbody>
</Table>
</div>
<div className="topicList">
<h3>Topics</h3>
<Table>
<thead>
<tr>
<td>Title</td>
<td>Discription</td>
<td>Delete</td>
</tr>
</thead>
<tbody>
{this.listTopics()}
</tbody>
</Table>
<Link to="/new-topic">
<Button variant="secondary" className="newTopic">Create a Topic</Button>
</Link>
</div>
</Container>
:
<Container>
<h1>You must be logged in and an Administrator to view this page. <Link to='/'>Return to Home</Link></h1>
</Container>
}
</Container>
)
};
}
const mapSateToProps = state => {
return {
isLoggedIn: state.login.loggedIn,
rank: state.login.user.rank
}
}
export default connect(mapSateToProps)(AdminPanel);
userModel.js
import mongoose from 'mongoose';
import bcrypt from 'bcrypt'
const Schema = mongoose.Schema;
const SALT_WORK_FACTOR = 10;
const userSchema = new Schema({
username: {
type: String,
required: true,
trim: true,
/*validate: {
validator: function(username) {User.doesNotExist({ username })},
message: "Username already exists"
},*/
minlength: 5
},
email: {
type: String,
/*validate: {
validator: function(email) {User.doesNotExist({ email })},
message: "Email already exists"
}*/
},
password: {type: String,
required: true,
index: {unique: true} },
avatar: {
type: String,
default: `${process.env.AWSFILESERICE}/pics/defaultPP.png`
},
isBanned: {
type: Boolean,
default: false
},
rank: {
type: String,
default: "Default"
},
joined: {
type: Date
},
topics: [{type: Schema.Types.ObjectId, ref: 'Topics'}],
threads: [{type: Schema.Types.ObjectId, ref: 'Topics.Threads'}],
posts: [{type: Schema.Types.ObjectId, ref: 'Topics.Threads.Posts'}]
}, { timestamps: true});
userSchema.pre('save', function(next) {
const user = this;
if (!user.isModified('password')) return next();
bcrypt.genSalt(SALT_WORK_FACTOR, function(err, salt) {
if (err) return next(err);
bcrypt.hash(user.password, salt, function(err, hash) {
if (err) return next(err);
user.password = hash;
next();
});
});
});
userSchema.statics.doesNotExist = async function (field) {
return await this.where(field).countDocuments() === 0;
}
userSchema.methods.authenticate = function(candidatePassword, cb) {
bcrypt.compare(candidatePassword, this.password, function(err, isMatch) {
if (isMatch === true) {
return cb(null, isMatch);
} else {
return cb();
}
})
};
const User = mongoose.model('User', userSchema);
export default User;
(ignore the commented out validation field, its an uncompleted experimentation :) )
Every other post to the update route (which should be "users/update/#id") works. i.e. username, password, email, even isBanned = true. Changing it back to false is not working. Yes, the unban button does "work" it sends the request to the backend like its supposed to and the backend sends "user updated" back to the frontend but the isBanned value stays the same in the database. I'm hoping I just overlooked something, but I read everything I could to solve the issue along with rereading my code... Thanks in advance for your help!

Solution
This solution should work if you want to use boolean values strictly,
... // Other user schema data
isBanned: String
Then when saving it use value.toString() to convert the boolean to string, so it saves as an string that contains true/false value, then when needed use
var originalValue = false;
var toSave = originalValue.toString();
console.log(toSave + ", It's a "+typeof toSave);
// When needed to check if the user is banned or not
// I'll ignore DB things
var isBanned = JSON.parse(toSave);
console.log(isBanned + ", It's a "+typeof isBanned);
For me is the easiest and the best solution

Related

not receiving jwt after logging in?

I've been trying to work on a movie app using the MERN stack. After having a few issues regarding the backend of one the features, I went back through my code and realised it doesnt seem that the jwt is being recieved after login. I have no issues with the signup and login other that it seemingly not obtaining the jwt
Heres the authJwt file
const jwt = require("jsonwebtoken");
const config = require("../config/auth.config.js");
const db = require("../models");
const User = db.user;
const Role = db.role;
verifyToken = (req, res, next) => {
let token = req.headers["x-access-token"];
if (!token) {
return res.status(403).send({ message: "No token provided!" });
}
jwt.verify(token, config.secret, (err, decoded) => {
if (err) {
return res.status(401).send({ message: "Unauthorized!" });
}
req.userId = decoded.id;
next();
});
};
isAdmin = (req, res, next) => {
User.findById(req.userId).exec((err, user) => {
if (err) {
res.status(500).send({ message: err });
return;
}
Role.find(
{
_id: { $in: user.roles }
},
(err, roles) => {
if (err) {
res.status(500).send({ message: err });
return;
}
for (let i = 0; i < roles.length; i++) {
if (roles[i].name === "admin") {
next();
return;
}
}
res.status(403).send({ message: "Require Admin Role!" });
return;
}
);
});
};
isModerator = (req, res, next) => {
User.findById(req.userId).exec((err, user) => {
if (err) {
res.status(500).send({ message: err });
return;
}
Role.find(
{
_id: { $in: user.roles }
},
(err, roles) => {
if (err) {
res.status(500).send({ message: err });
return;
}
for (let i = 0; i < roles.length; i++) {
if (roles[i].name === "moderator") {
next();
return;
}
}
res.status(403).send({ message: "Require Moderator Role!" });
return;
}
);
});
};
const authJwt = {
verifyToken,
isAdmin,
isModerator
};
module.exports = authJwt;
Heres the user routes file. When I go to http://localhost:8081//api/test/user after logging in, it displays the message "No token provided!"
const { authJwt } = require("../middlewares");
const controller = require("../controllers/user.controller");
module.exports = function(app) {
app.use(function(req, res, next) {
res.header(
"Access-Control-Allow-Headers",
"x-access-token, Origin, Content-Type, Accept"
);
next();
});
app.get("/api/test/all", controller.allAccess);
app.get("/api/test/user", [authJwt.verifyToken], controller.userBoard);
app.get(
"/api/test/mod",
[authJwt.verifyToken, authJwt.isModerator],
controller.moderatorBoard
);
app.get(
"/api/test/admin",
[authJwt.verifyToken, authJwt.isAdmin],
controller.adminBoard
);
};
Here's my server.js file
const express = require("express");
const cors = require("cors");
const dbConfig = require("./app/config/db.config");
const app = express();
var corsOptions = {
origin: "http://localhost:8081"
};
app.use(cors(corsOptions));
// parse requests of content-type - application/json
app.use(express.json());
// parse requests of content-type - application/x-www-form-urlencoded
app.use(express.urlencoded({ extended: true }));
const db = require("./app/models");
const Role = db.role;
db.mongoose
.connect(`mongodb+srv://tami00:MEUxClWqUNbLz359#cluster0.gmvao.mongodb.net/test?retryWrites=true&w=majority`, {
useNewUrlParser: true,
useUnifiedTopology: true
})
.then(() => {
console.log("Successfully connect to MongoDB.");
initial();
})
.catch(err => {
console.error("Connection error", err);
process.exit();
});
// simple route
app.use('/api/favourite', require('./app/routes/favourite.routes'));
// routes
// require(".app/routes/favourite.routes")(app);
require("./app/routes/auth.routes")(app);
require("./app/routes/user.routes")(app);
// set port, listen for requests
const PORT = process.env.PORT || 8080;
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}.`);
});
function initial() {
Role.estimatedDocumentCount((err, count) => {
if (!err && count === 0) {
new Role({
name: "user"
}).save(err => {
if (err) {
console.log("error", err);
}
console.log("added 'user' to roles collection");
});
new Role({
name: "creator"
}).save(err => {
if (err) {
console.log("error", err);
}
console.log("added 'creator' to roles collection");
});
new Role({
name: "watcher"
}).save(err => {
if (err) {
console.log("error", err);
}
console.log("added 'watcher' to roles collection");
});
}
});
}
FRONTEND
AuthService
import axios from "axios";
const API_URL = "http://localhost:8080/api/auth/";
class AuthService {
login(username, password) {
return axios
.post(API_URL + "signin", {
username,
password
})
.then(response => {
if (response.data.accessToken) {
localStorage.setItem("user", JSON.stringify(response.data));
}
return response.data;
});
}
logout() {
localStorage.removeItem("user");
}
register(username, email, password) {
return axios.post(API_URL + "signup", {
username,
email,
password
});
}
getCurrentUser() {
return JSON.parse(localStorage.getItem('user'));;
}
}
export default new AuthService();
Auth Header
export default function authHeader() {
const user = JSON.parse(localStorage.getItem('user'));
if (user && user.accessToken) {
// return { Authorization: 'Bearer ' + user.accessToken }; // for Spring Boot back-end
return { 'x-access-token': user.accessToken }; // for Node.js Express back-end
} else {
return {};
}
}
Login Component
import React, { Component } from "react";
import Form from "react-validation/build/form";
import Input from "react-validation/build/input";
import CheckButton from "react-validation/build/button";
import AuthService from "../services/auth.service";
const required = value => {
if (!value) {
return (
<div className="alert alert-danger" role="alert">
This field is required!
</div>
);
}
};
export default class Login extends Component {
constructor(props) {
super(props);
this.handleLogin = this.handleLogin.bind(this);
this.onChangeUsername = this.onChangeUsername.bind(this);
this.onChangePassword = this.onChangePassword.bind(this);
this.state = {
username: "",
password: "",
loading: false,
message: ""
};
}
onChangeUsername(e) {
this.setState({
username: e.target.value
});
}
onChangePassword(e) {
this.setState({
password: e.target.value
});
}
handleLogin(e) {
e.preventDefault();
this.setState({
message: "",
loading: true
});
this.form.validateAll();
if (this.checkBtn.context._errors.length === 0) {
AuthService.login(this.state.username, this.state.password).then(
() => {
this.props.history.push("/profile");
window.location.reload();
},
error => {
const resMessage =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
this.setState({
loading: false,
message: resMessage
});
}
);
} else {
this.setState({
loading: false
});
}
}
render() {
return (
<div className="col-md-12">
<div className="card card-container">
<img
src="//ssl.gstatic.com/accounts/ui/avatar_2x.png"
alt="profile-img"
className="profile-img-card"
/>
<Form
onSubmit={this.handleLogin}
ref={c => {
this.form = c;
}}
>
<div className="form-group">
<label htmlFor="username">Username</label>
<Input
type="text"
className="form-control"
name="username"
value={this.state.username}
onChange={this.onChangeUsername}
validations={[required]}
/>
</div>
<div className="form-group">
<label htmlFor="password">Password</label>
<Input
type="password"
className="form-control"
name="password"
value={this.state.password}
onChange={this.onChangePassword}
validations={[required]}
/>
</div>
<div className="form-group">
<button
className="btn btn-primary btn-block"
disabled={this.state.loading}
>
{this.state.loading && (
<span className="spinner-border spinner-border-sm"></span>
)}
<span>Login</span>
</button>
</div>
{this.state.message && (
<div className="form-group">
<div className="alert alert-danger" role="alert">
{this.state.message}
</div>
</div>
)}
<CheckButton
style={{ display: "none" }}
ref={c => {
this.checkBtn = c;
}}
/>
</Form>
</div>
</div>
);
}
}

Filtering Products by Price is not working MERN Stack

Hi I am building an Ecommerce Application using MERN Stack with redux. But having issues when i try to send price array arguement in my fetching function. It is giving me the error that XHR GET http://localhost:3000/api/v1/products?keyword=&page=1&price[gt]=undefined&price[lt]=undefined
[HTTP/1.1 400 Bad Request 12ms]. Here is my Products.jsx Component Code {success:false,message:'Resource not found invalid Price'}
import React, { useEffect, useState } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { Slider, Typography } from '#material-ui/core';
import { useAlert } from 'react-alert';
import { clearErrors, getProducts } from '../../actions/productActions';
import Loader from '../layout/Loader/Loader';
import ProductCard from '../Home/ProductCard';
import './Products.css';
import Pagination from 'react-js-pagination';
const Products = ({ match }) => {
const dispatch = useDispatch();
const alert = useAlert();
const [currentPage, setCurrentPage] = useState(1);
const [price, setPrice] = useState(0, 25000);
const { loading, products, productsCount, error, resultPerPage} =
useSelector((state) => state.products);
const keyword = match.params.keyword;
const setCurrentPageNo = (e) => {
setCurrentPage(e);
};
const priceHandler = (e, newPrice) => {
setPrice(newPrice);
};
useEffect(() => {
if (error) {
alert.error(error);
dispatch(clearErrors());
}
dispatch(getProducts(keyword, currentPage, price));
}, [dispatch, keyword, currentPage]);
return (
<>
{loading ? (
<Loader />
) : (
<>
<h2 className='productsHeading'>Products</h2>
<div className='products'>
{products &&
products.map((product) => (
<ProductCard key={product._id} product={product} />
))}
</div>
<div className='filterBox'>
<Typography>Price</Typography>
<Slider
value={price}
onChange={priceHandler}
valueLabelDisplay='auto'
aria-labelledby='range-slider'
min={0}
max={25000}
/>
</div>
{resultPerPage < productsCount && (
<div className='paginationBox'>
<Pagination
activePage={currentPage}
itemsCountPerPage={resultPerPage}
totalItemsCount={productsCount}
onChange={setCurrentPageNo}
nextPageText='Next'
prevPageText='Prev'
firstPageText='1st'
lastPageText='Last'
itemClass='page-item'
linkClass='page-link'
activeClass='pageItemActive'
activeLinkClass='pageLinkActive'
/>
</div>
)}
</>
)}
</>
);
};
export default Products;
And here is my action for getting allproducts
import axios from 'axios';
import {
ALL_PRODUCT_REQUEST,
ALL_PRODUCT_SUCCESS,
ALL_PRODUCT_FAIL,
PRODUCT_DETAILS_REQUEST,
PRODUCT_DETAILS_SUCCESS,
PRODUCT_DETAILS_FAIL,
CLEAR_ERRORS,
} from '../constants/productConstants';
export const getProducts =
(keyword = '', currentPage = 1, price = [0, 25000]) =>
async (dispatch) => {
try {
dispatch({
type: ALL_PRODUCT_REQUEST,
});
const { data } = await axios.get(
`/api/v1/products?keyword=${keyword}&page=${currentPage}&price[gt]=${price[0]}&price[lt]=${price[1]}`
);
dispatch({
type: ALL_PRODUCT_SUCCESS,
payload: data,
});
} catch (error) {
dispatch({
type: ALL_PRODUCT_FAIL,
payload: error.response.data.message,
});
}
};
Here is the Reducer for Products
export const productReducer = (state = { products: [] }, action) => {
switch (action.type) {
case ALL_PRODUCT_REQUEST:
return {
loading: true,
products: [],
};
case ALL_PRODUCT_SUCCESS:
return {
loading: false,
products: action.payload.products,
productsCount: action.payload.productsCount,
resultPerPage: action.payload.resultPerPage,
};
case ALL_PRODUCT_FAIL:
return {
loading: false,
error: action.payload,
};
case CLEAR_ERRORS:
return {
...state,
error: null,
};
default:
return state;
}
};
I am Using Custom Error Handling Class and here is the code for this
class ErrorResponse extends Error {
constructor(message, statusCode) {
super(message);
this.statusCode = statusCode;
Error.captureStackTrace(this, this.constructor);
}
}
module.exports = ErrorResponse;
And here is the middleware function
const ErrorResponse = require('../utils/errorResponse');
const errorHandler = (err, req, res, next) => {
err.statusCode = err.statusCode || 500;
err.message = err.message || 'Internal Server Error';
// Wrong Mongodb Id error
if (err.name === 'CastError') {
const message = `Resource not found. Invalid: ${err.path}`;
err = new ErrorResponse(message, 400);
}
// Mongoose duplicate key error
if (err.code === 11000) {
const message = `Duplicate ${Object.keys(err.keyValue)} Entered`;
err = new ErrorResponse(message, 400);
}
// Wrong JWT error
if (err.name === 'JsonWebTokenError') {
const message = `Json Web Token is invalid, Try again `;
err = new ErrorResponse(message, 400);
}
// JWT EXPIRE error
if (err.name === 'TokenExpiredError') {
const message = `Json Web Token is Expired, Try again `;
err = new ErrorResponse(message, 400);
}
res.status(err.statusCode).json({
success: false,
message: err.message,
});
};
module.exports = errorHandler;
Here is my ApiFeatures class for filtering, Pagination and Sorting
class ApiFeatures {
constructor(query, queryStr) {
this.query = query;
this.queryStr = queryStr;
}
search() {
const keyword = this.queryStr.keyword
? {
name: {
$regex: this.queryStr.keyword,
$options: 'i',
},
}
: {};
this.query = this.query.find({ ...keyword });
return this;
}
filter() {
const queryCopy = { ...this.queryStr };
//Removing fields
const removFields = ['keyword', 'page', 'limit'];
removFields.forEach((key) => delete queryCopy[key]);
let queryStr = JSON.stringify(queryCopy);
queryStr = queryStr.replace(/\b(gt|gte|lt|lte)\b/g, (key) => `$${key}`);
this.query = this.query.find(JSON.parse(queryStr));
return this;
}
pagination(resultPerPage) {
const currentPage = Number(this.queryStr.page) || 1;
const skip = resultPerPage * (currentPage - 1);
this.query = this.query.limit(resultPerPage).skip(skip);
return this;
}
}
module.exports = ApiFeatures;
Here is my controller function
const getProducts = asyncHandler(async (req, res, next) => {
const resultPerPage = 8;
const productsCount = await Product.countDocuments();
const apiFeatures = new ApiFeatures(Product.find(), req.query)
.search()
.filter()
.pagination(resultPerPage);
const products = await apiFeatures.query;
res.status(200).json({
success: true,
count: products.length,
productsCount,
resultPerPage,
products,
});
});
My Product Model
const mongoose = require('mongoose');
const productSchema = mongoose.Schema({
name: {
type: String,
required: [true, 'Please Enter product Name'],
trim: true,
},
description: {
type: String,
required: [true, 'Please Enter product Description'],
},
price: {
type: Number,
required: [true, 'Please Enter product Price'],
maxLength: [8, 'Price cannot exceed 8 characters'],
},
ratings: {
type: Number,
default: 0,
},
images: [
{
public_id: {
type: String,
required: true,
},
url: {
type: String,
required: true,
},
},
],
category: {
type: String,
required: [true, 'Please Enter Product Category'],
},
Stock: {
type: Number,
required: [true, 'Please Enter product Stock'],
maxLength: [4, 'Stock cannot exceed 4 characters'],
default: 1,
},
numOfReviews: {
type: Number,
default: 0,
},
reviews: [
{
user: {
type: mongoose.Schema.ObjectId,
ref: 'User',
required: true,
},
name: {
type: String,
required: true,
},
rating: {
type: Number,
required: true,
},
comment: {
type: String,
required: true,
},
},
],
user: {
type: mongoose.Schema.ObjectId,
ref: 'User',
required: true,
},
createdAt: {
type: Date,
default: Date.now,
},
});
module.exports = mongoose.model('Product', productSchema);
I am not figuring it out that what is the issue coz i have tested this api on postman and it is giving correct results. Help me to resolve this issue
By a first look, the url on which you're sending the request is http://localhost:3000/api/v1/products?keyword=&page=1&price[gt]=undefined&price[lt]=undefined.
I feel the problem is generated by sending undefined values.
First, initialize price in your state to an array of 2 elements if you're accessing that way in the action.
const [price, setPrice] = useState([0, 25000]);
Second, make sure you are getting the right value from your filter
const priceHandler = (e, newPrice) => {
console.log(newPrice) // Maybe the value you need isn't being passed in the call
setPrice(newPrice);
};
Also, you can inspect the value of price coming in the action and see if it's really an array.
export const getProducts =
(keyword = '', currentPage = 1, price = [0, 25000]) =>
async (dispatch) => {
console.log(price) // and look at the value here
try {
dispatch({
type: ALL_PRODUCT_REQUEST,
});
const { data } = await axios.get(
`/api/v1/products?keyword=${keyword}&page=${currentPage}&price[gt]=${price[0]}&price[lt]=${price[1]}`
);
// Rest of the function ...
};

React app requires hard refresh after successful login

I'm struggling to debug a problem and I'd appreciate any help the community might be able to offer. I'm building my first React app and have built a working Login feature, but after every successful login the user is forced to hard refresh his/her browser in order see the app in a "logged in" state. There is no error logged to the browser console, but our DevTools monitor shows the following error:
"TypeError: Cannot read property 'setState' of undefined"
What's funny is that the login authentication first succeeds, and then immediately seems to try again and fails. After clicking "login," the user must hard refresh the web page in order to make it appear that the login has worked.
I'm stumped. Can anyone see anything wrong with my code? Thank you very much in advance for taking a look!
Here's our LoginPage jsx file that contains the actual login web form:
import React from 'react';
import { Button, Form, FormGroup, Label, Input } from 'reactstrap';
export default class LoginPage extends React.Component {
constructor(props) {
super(props);
//bound functions
this.compileFormData = this.compileFormData.bind(this);
this.handleEmailChange = this.handleEmailChange.bind(this);
this.handlePasswordChange = this.handlePasswordChange.bind(this);
//component state
this.state = {
email: '',
password: '',
};
}
//update state as email value changes
handleEmailChange(e) {
this.setState({ email: e.target.value });
}
//update state as password value changes
handlePasswordChange(e) {
this.setState({ password: e.target.value });
}
compileFormData() {
const { loginFunction } = this.props;
const formData = this.state;
loginFunction(formData);
}
render() {
return (
<div className="row justify-content-center">
<div className="col-10 col-sm-7 col-md-5 col-lg-4">
<Form>
<FormGroup>
<Label for="exampleEmail">Email</Label>
<Input
type="email"
name="email"
id="userEmail"
placeholder="test#mccre.com"
value={this.state.email}
onChange={this.handleEmailChange}
/>
</FormGroup>
<FormGroup>
<Label for="examplePassword">Password</Label>
<Input
type="password"
name="password"
id="userPassword"
placeholder="password"
value={this.state.password}
onChange={this.handlePasswordChange}
/>
</FormGroup>
<Button onClick={this.compileFormData}>Log In</Button>
</Form>
</div>
</div>
);
}
}
Here's our login page Container that renders the login page:
import React, { Component } from 'react';
import { Redirect } from 'react-router-dom';
import { connect } from 'react-redux';
import { logUserIn } from '../../actions/authentication';
import LoginPage from './LoginPage';
export class LoginPageContainer extends React.Component {
constructor(props) {
super(props);
//bound functions
this.logUserInFunction = this.logUserInFunction.bind(this);
}
logUserInFunction(userData) {
const { dispatch } = this.props;
dispatch(logUserIn(userData));
}
render() {
const { authentication } = this.props;
if (authentication.isLoggedIn) {
return (
<Redirect to="/" />
);
}
return (
<div>
<LoginPage loginFunction={this.logUserInFunction} />
</div>
);
}
}
function mapStateToProps(state) {
return {
authentication: state.authentication,
};
}
export default connect(mapStateToProps)(LoginPageContainer);
Here's our API endpoint in which we actually make the database query:
const express = require('express');
const mongoose = require('mongoose');
const passport = require('passport');
const User = require('../../models/user.js');
const router = express.Router();
//configure mongoose promises
mongoose.Promise = global.Promise;
//POST to /register
router.post('/register', (req, res) => {
//Create a user object to save, using values from incoming JSON
const newUser = new User({
username: req.body.username,
firstName: req.body.firstName,
lastname: req.body.lastName,
email: req.body.email,
});
//Save, via passport's "register" method, the user
User.register(newUser, req.body.password, (err, user) => {
//If there's a problem, send back a JSON object with the error
if (err) {
return res.send(JSON.stringify({ error: err }));
}
// Otherwise, for now, send back a JSON object with the new user's info
return res.send(JSON.stringify(user));
});
});
//POST to /login
router.post('/login', async (req, res) => {
//look up user by their email
const query = User.findOne({ email: req.body.email });
const foundUser = await query.exec();
//If they exist, they'll have a username, so add that to our body
if (foundUser) {
req.body.username = foundUser.username;
}
passport.authenticate('local') (req, res, () => {
//If logged in, we should have use info to send back
if (req.user) {
return res.send(JSON.stringify(req.user));
}
//Otherwise return an error
return res.send(JSON.stringify({ error: 'There was an error logging in' }));
});
});
//GET to /checksession
router.get('/checksession', (req, res) => {
if (req.user) {
return res.send(JSON.stringify(req.user));
}
return res.send(JSON.stringify({}));
});
//GET to /logout
router.get('/logout', (req, res) => {
req.logout();
return res.send(JSON.stringify(req.user));
});
module.exports = router;
Here's the action file in which we define the logUserIn() function:
import { decrementProgress, incrementProgress } from './progress';
import 'whatwg-fetch';
//Action Creators
export const loginAttempt = () => ({ type: 'AUTHENTICATION_LOGIN_ATTEMPT' });
export const loginFailure = error => ({ type: 'AUTHENTICATION_LOGIN_FAILURE', error });
export const loginSuccess = json => ({ type: 'AUTHENTICATION_LOGIN_SUCCESS', json });
export const logoutFailure = error => ({ type: 'AUTHENTICATION_LOGOUT_FAILURE', error });
export const logoutSuccess = () => ({ type: 'AUTHENTICATION_LOGOUT_SUCCESS' });
export const sessionCheckFailure = () => ({ type: 'AUTHENTICATION_SESSION_CHECK_FAILURE'});
export const sessionCheckSuccess = json => ({ type: 'AUTHENTICATION_SESSION_CHECK_SUCCESS', json });
//Check User Session
export function checkSession() {
return async (dispatch) => {
//contact the API
await fetch(
//where to contact
'/api/authentication/checksession',
//what to send
{
method: 'GET',
credentials: 'same-origin',
},
)
.then((response) => {
if (response.status === 200) {
return response.json();
}
return null;
})
.then((json) => {
if (json.username) {
return dispatch(sessionCheckSuccess(json));
}
return dispatch(sessionCheckFailure());
})
.catch((error) => dispatch(sessionCheckFailure(error)));
};
}
//Log user in
export function logUserIn(userData) {
return async (dispatch) => {
//turn on spinner
dispatch(incrementProgress());
//register that a login attempt is being made
dispatch(loginAttempt());
//contact login API
await fetch(
//where to contact
'http://localhost:3000/api/authentication/login',
//what to send
{
method: 'POST',
body: JSON.stringify(userData),
headers: {
'Content-Type': 'application/json',
},
credentials: 'include',
},
).then((response) => {
if (response.status === 200) {
return response.json();
}
return null;
})
.then((json) => {
if (json) {
dispatch(loginSuccess(json));
this.setState({ redirect: true });
} else {
dispatch(loginFailure(new Error('Authentication Failed')));
}
}).catch((error) => {
dispatch(loginFailure(new Error(error)));
});
//turn off spinner
dispatch(decrementProgress());
};
}
//Log user out
export function logUserOut() {
return async (dispatch) => {
//turn on spinner
dispatch(incrementProgress());
//contact the API
await fetch(
//where to contact
'/api/authentication/logout',
//what to send
{
method: 'GET',
credentials: 'same-origin',
},
)
.then((response) => {
if (response.status === 200) {
dispatch(logoutSuccess());
} else {
dispatch(logoutFailure(`Error: ${response.status}`));
}
})
.catch((error) => {
dispatch(logoutFailure(error));
});
//turn off spinner
return dispatch(decrementProgress());;
};
}
Finally, here's the reducer file that is supposed to update the application's state depending on authentication success / failure:
const initialState = {
firstName: '',
id: '',
isLoggedIn: false,
isLoggingIn: false,
lastName: '',
username: '',
};
export default function reducer(state = initialState, action) {
switch (action.type) {
case 'AUTHENTICATION_LOGIN_ATTEMPT': {
const newState = Object.assign({}, state);
newState.isLoggingIn = true;
return newState;
}
case 'AUTHENTICATION_LOGIN_FAILURE':
case 'AUTHENTICATION_SESSION_CHECK_FAILURE':
case 'AUTHENTICATION_LOGOUT_SUCCESS': {
const newState = Object.assign({}, initialState);
return newState;
}
case 'AUTHENTICATION_LOGIN_SUCCESS':
case 'AUTHENTICATION_SESSION_CHECK_SUCCESS': {
const newState = Object.assign({}, state);
newState.firstName = action.json.firstName;
newState.id = action.json._id;
newState.isLoggedIn = true;
newState.isLoggingIn = false;
newState.lastName = action.json.lastName;
newState.username = action.json.username;
return newState;
}
case 'AUTHENTICATION_LOGOUT_FAILURE': {
//todo: hanle error
return state;
}
default: {
return state;
}
}
}
Found the solution: "the this.setState({ redirect: true });" line needed to be removed from the action file.

React. Can't acces API data on local 5000. Database from CSV file. Promise undefined response

I've been going crazy for a couple of days with an unresolved issue. Please help.
Working with a CSV database here. Creating own API. Small React App Fullstack MERN.
I'm trying to get to show all the employeees from my database.
I wrote the backend with express and have all the data now showing in json format on localhost:5000/employees
Also created a context on the front to deal with that data and reducer file for the functions.
Problem I have is I'm not beeing able to use the data on the ContextProvider file. ('EmployeesState')
Promise of my async function keeps giving me an undefined response. Tried both with fetch and axios but is not working.
Some of the code is from a previous project i did and it worked there so i 'm going crazy here. I can't seem to solve it. Please help.
BACKEND
db.js
const parse = require("csv-parse");
const fs = require("fs");
const employeesData = [];
const connectDB = () => {
fs.createReadStream(__dirname + "/employees1.txt")
.pipe(
parse({
delimiter: ",",
columns: true,
})
)
.on("data", (dataRow) => {
employeesData.push(dataRow);
})
.on("end", () => {
console.log(employeesData);
});
};
connectDB();
// console.log(connectDB());
module.exports = connectDB;
module.exports = employeesData;
server.js
var createError = require("http-errors");
var express = require("express");
const bodyParser = require("body-parser");
var cookieParser = require("cookie-parser");
const logger = require("morgan");
const cors = require("cors");
const connectDB = require("./config/db");
const employeesData = require("./config/db");
var path = require("path");
var app = express();
// const config = require("config");
//Connect DB
// connectDB();
let dataBase = employeesData;
console.log(employeesData);
app.use(cors());
// app.use(bodyParser.json()); //here
// Initial Middleware
//By doing this we can accepta data. Using the req.body
app.use(express.json({ extended: false }));
///Define Routes
app.use("/", require("./routes/index"));
app.use("/employees", require("./routes/employees"));
//Serve React in production
if (process.env.NODE_ENV === "production") {
//Set static folder (build folder)
app.use(express.static("client/build"));
app.get("*", (req, res) =>
res.sendFile(path.resolve(__dirname, "client", "build", "index.html"))
);
}
// app.engine("html", require("ejs").renderFile);
app.set("view engine", "html");
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(`Server started on port ${PORT}`));
employees.js
const express = require("express");
const router = express.Router();
const config = require("config");
const { check, validationResult } = require("express-validator");
const Employee = require("../models/EmployeeModel");
const employeesData = require("../config/db");
// router.get("/", function (req, res) {
// res.send({ employeesData });
// });
//#route GET api/employees
//#desc Get all users Employees
//#access Private
router.get("/", async (req, res) => {
try {
const employees = await employeesData;
res.json({ employees });
} catch (err) {
console.error(err.message);
res.status(500).send("Server error");
}
});
//#route POST api/employees
//#desc Add new Employee
//#access Private
router.post(
"/",
[
check("name", "Name is required").not().isEmpty(),
check("surname", "Surname is required").not().isEmpty(),
],
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const { name, surname, adress, phone, email, birthdate } = req.body;
try {
const newEmployee = new Employee({
name,
surname,
adress,
phone,
email,
birthdate,
});
const employee = await newEmployee.save();
res.json(employee);
} catch (err) {
console.error(err.message);
res.status(500).send("Server Error");
}
}
);
//#route DELETE api/employee
//#desc Delete Employee
router.delete("/:id", async (req, res) => {
try {
let employee = await Employee.findById(req.params.id);
if (!employee) return res.status(404).json({ msg: "Employee not found" });
await Employee.findByIdAndRemove(req.params.id);
res.json({ msg: `Employee removed` });
} catch (err) {
console.error(err.message);
res.status(500).send("Server Error");
}
});
module.exports = router;
FRONTEND
EmployeesState.js
import React, { useReducer, useState } from "react";
import axios from "axios";
import { v4 as uuid } from "uuid";
import EmployeesContext from "./employeesContext";
import employeesReducer from "./employeesReducer";
import {
ADD_EMPLOYEE,
DELETE_EMPLOYEE,
SET_CURRENT,
CLEAR_CURRENT,
UPDATE_EMPLOYEE,
FILTER_EMPLOYEES,
EMPLOYEE_ERROR,
CLEAR_FILTER,
GET_EMPLOYEES,
CLEAR_EMPLOYEES,
} from "../types";
const EmployeesState = (props) => {
const initialState = {
employees: [],
current: null,
filtered: null,
error: null,
loading: false,
};
const [state, dispatch] = useReducer(employeesReducer, initialState);
const [employees, setEmployees] = useState(initialState);
//Get Employees
// // gives me promise{<prending>} on console
const getEmployees = async () => {
try {
const res = axios.get("http://localhost:5000/employees");
dispatch({ type: GET_EMPLOYEES, payload: res });
} catch (err) {
dispatch({
type: EMPLOYEE_ERROR,
});
}
};
//trying with fetch. throwing me undefined on console
const callAPI = () => {
fetch("http://localhost:5000/employees")
.then((res) => res.json())
.then((res) =>
setEmployees({
...initialState,
loading: false,
employees: res,
})
);
};
//Add Employee
const addEmployee = async (employee) => {
const config = {
headers: {
"Content-Type": "application/json",
},
};
try {
const res = await axios.post("/employees", employee, config);
dispatch({ type: ADD_EMPLOYEE, payload: res.data });
} catch (err) {
dispatch({
type: EMPLOYEE_ERROR,
payload: err.response.data.msg,
});
}
};
//Delete Employee
const deleteEmployee = async (id) => {
try {
await axios.delete(`/employees/${id}`);
dispatch({ type: DELETE_EMPLOYEE, payload: id });
} catch (err) {
dispatch({
type: EMPLOYEE_ERROR,
payload: err.response.msg,
});
}
};
//Update Employee
const updateEmployee = async (employee) => {
const config = {
headers: {
"Content-Type": "application/json",
},
};
try {
const res = await axios.put(
`/api/employees/${employee._id}`,
employee,
config
);
dispatch({ type: UPDATE_EMPLOYEE, payload: res.data });
} catch (err) {
dispatch({
type: EMPLOYEE_ERROR,
payload: err.response.msg,
});
}
dispatch({ type: UPDATE_EMPLOYEE, payload: employee });
};
//Clear Employees
const clearEmployees = () => {
dispatch({ type: CLEAR_EMPLOYEES });
};
//Set Current Employees
const setCurrent = (employee) => {
dispatch({ type: SET_CURRENT, payload: employee });
};
//Clear Current Employee
const clearCurrent = () => {
dispatch({ type: CLEAR_CURRENT });
};
//Filter Employees
const filterEmployees = (text) => {
dispatch({ type: FILTER_EMPLOYEES, payload: text });
};
//Clear Filter
const clearFilter = (text) => {
dispatch({ type: CLEAR_FILTER });
};
return (
<EmployeesContext.Provider
value={{
employees: state.employees,
current: state.current,
filtered: state.filtered,
error: state.error,
loading: state.loading,
callAPI,
getEmployees,
addEmployee,
deleteEmployee,
clearEmployees,
setCurrent,
clearCurrent,
updateEmployee,
filterEmployees,
clearFilter,
}}
>
{props.children}
</EmployeesContext.Provider>
);
};
export default EmployeesState;
// // gives me undefined on console
// const getEmployees = () => {
// axios
// .get("http://localhost:5000/employees")
// .then((res) => dispatch({ type: GET_EMPLOYEES, payload: res }))
// .catch((err) => {
// dispatch({
// type: EMPLOYEE_ERROR,
// });
// });
// };
Home.js
import React, { Fragment } from "react";
import Search from "../employees/Search";
import Employees from "../employees/Employees";
const Home = () => {
return (
<>
<Search />
<Employees />
</>
);
};
export default Home;
Employees.js
import React, { useContext, useEffect } from "react";
import { CSSTransition, TransitionGroup } from "react-transition-group";
import EmployeeItem from "./EmployeeItem";
import { v4 as uuidv4 } from "uuid";
import EmployeesContext from "../../contexts/employees/employeesContext";
import Spinner from "../layout/Spinner";
const Employees = () => {
const employeesContext = useContext(EmployeesContext);
const {
employees,
filtered,
getEmployees,
callAPI,
loading,
} = employeesContext;
useEffect(() => {
// callAPI();
getEmployees();
// eslint-disable-next-line
console.log(employees);
console.log(getEmployees());
console.log(callAPI());
}, []);
return (
<div>
<>
{[employees].map((employee) => (
<EmployeeItem key={uuidv4()} employee={employee} />
))}
</>
</div>
);
employeesReducer.js
import {
GET_EMPLOYEES,
ADD_EMPLOYEE,
DELETE_EMPLOYEE,
SET_CURRENT,
CLEAR_CURRENT,
UPDATE_EMPLOYEE,
FILTER_EMPLOYEES,
CLEAR_FILTER,
EMPLOYEE_ERROR,
CLEAR_EMPLOYEES,
} from "../types";
export default (state, action) => {
switch (action.type) {
case GET_EMPLOYEES:
return {
...state,
employees: action.payload,
loading: false,
};
case ADD_EMPLOYEE:
return {
...state,
employee: [action.payload, ...state.employees],
loading: false,
};
case UPDATE_EMPLOYEE:
return {
...state,
employees: state.employees.map((employee) =>
employee._id === action.payload._id ? action.payload : employee
),
loading: false,
};
case DELETE_EMPLOYEE:
return {
...state,
employees: state.employees.filter(
(employee) => employee._id !== action.payload
),
loading: false,
};
case CLEAR_EMPLOYEES:
return {
...state,
employees: null,
filtered: null,
error: null,
current: null,
};
case SET_CURRENT:
return {
...state,
current: action.payload,
};
case CLEAR_CURRENT:
return {
...state,
current: null,
};
case FILTER_EMPLOYEES:
return {
...state,
filtered: state.employees.filter((employee) => {
const regex = new RegExp(`${action.payload}`, "gi");
return employee.name.match(regex) || employee.email.match(regex);
}),
};
case CLEAR_FILTER:
return {
...state,
filtered: null,
};
case EMPLOYEE_ERROR:
return {
...state,
error: action.payload,
};
default:
return state;
}
};
You need to await for the response
const res = await axios.get("http://localhost:5000/employees");
dispatch({ type: GET_EMPLOYEES, payload: res });
or
axios.get("http://localhost:5000/employees")
.then(res => {
dispatch({ type: GET_EMPLOYEES, payload: res });
})

ECONNREFUSED - node.js, passport.js, create-react-app, custom middleware

y'all, I'm trying to create a signup and login local-Strategy with passport and have been seeing a PROXY ERROR:. I thought I was returning all possible endings for the functions but maybe I'm missing something? I'm new to development and am going crazy haha... Any help is much appreciated, also let me know if I missed adding pertinent code. (I did not add the return block from the react component, I assumed it unnecessary). I am importing my strategies through a strategy index.js (also did not include).
SignupStratagy.js / local strategy
const Strategy = require("passport-local").Strategy;
const User = require("../models/User");
// for password encryption;
const bcrypt = require("bcrypt");
const saltRounds = 10;
const SignupStrategy = new Strategy({ passReqToCallback: true }, function (
req,
username,
password,
done
) {
// console.log(username, password);
const encryptedPass = bcrypt.hashSync(password, saltRounds);
// console.log("encryptedPassword", encryptedPass);
const phone = req.body.phone;
const email = req.body.email;
const street = req.body.street;
const city = req.body.city;
const state = req.body.state;
const zip = req.body.zip;
const isAdmin = req.body.isAdmin;
User.findOne({ username: username }, (err, user) => {
// console.log("SignupStrategy.js / req:", req.body);
if (err) {
return done(err, user);
}
if (user) {
return done("User Name is already taken:", user);
}
})
// .lean()
// .exec();
// console.log("SignupStrategy.js / encrypted password:", encryptedPass);
let newUser = {
username,
password: encryptedPass,
phone,
email,
street,
city,
state,
zip,
isAdmin,
};
User.create(newUser, (error, newUser) => {
if (error) {
return done(error, null);
}
// delete the user password before it is sent back to the front-end;
newUser.password = undefined;
delete newUser.password;
return done(null, newUser);
});
});
module.exports = SignupStrategy;
apiRoute.js / route for signup
const router = require("express").Router();
const db = require("../models");
const passport = require("../config");
// const isAuthenticated = require("../config/middleware/isAuthenticated");
// USER SIGN-UP ROUTE
router.post("/api/signup", function (req, res, next) {
// console.log(req.body);
passport.authenticate("local-signup", (error, user) => {
// console.log("apiRoutes.js / error:", error, "apiRoutes.js / user:", user)
if (error)
return res.status(500).json({
message: error,
});
else {
return res.status(200).json(user);
}
})(req, res, next);
});
Signup / react component
import React, { useState } from "react";
import axios from "axios";
import { Redirect, Link } from "react-router-dom";
import chip from "../../images/chipper/chipperOne.png";
import "./style.css";
function Signup() {
const [signupState, setSignupState] = useState({
username: "",
password: "",
phone: "",
email: "",
street: "",
city: "",
state: "",
zip: "",
key: "",
redirect: false,
adminRedirect: false,
isAdmin: false,
});
const onChange = (e) => {
// console.log("working")
// console.log(typeof e.target.type)
if (e.target.type === "checkbox") {
if (signupState.isAdmin === false) {
setSignupState({
...signupState,
isAdmin: true,
})
} else {
setSignupState({
...signupState,
isAdmin: false,
});
}
} else {
setSignupState({
...signupState,
[e.target.name]: e.target.value,
});
}
};
const onSubmit = async (e) => {
e.preventDefault();
if (signupState.isAdmin === true) {
const response = await axios.post("/api/admin-sign-up", {
key: signupState.key,
});
console.log(response.status);
if (response.status === 500) {
console.log(response);
return;
}
if (response.status === 200) {
console.log(response.status);
setSignupState({
...signupState,
adminRedirect: true,
});
}
}
// console.log(`onSubmit ${signupState}`);
axios.post("/api/signup", {
username: signupState.username,
password: signupState.password,
phone: signupState.phone,
email: signupState.email,
street: signupState.street,
city: signupState.city,
state: signupState.state,
zip: signupState.zip,
isAdmin: signupState.isAdmin,
})
.then((res) => {
console.log(res);
if (res.data) {
console.log(`Sign-in Successful`);
setSignupState({
...signupState,
redirect: true,
});
}
})
.catch((err) => {
if (err) console.log(`Sign-Up server error ${err}`);
});
};
ERROR:
Proxy error: Could not proxy request /api/signup from localhost:3000 to http://localhost:3001.
[1] See https://nodejs.org/api/errors.html#errors_common_system_errors for more information (ECONNREFUSED).

Categories