I'm making a full stack Rick and Morty application. Characters on the screen and the user can login and click on them to add them to favorites and then click on them on the favorites page to delete them from the favorites page.
The application works but crashes after a few minutes saying that a fetch request didn't work. In network section of the developer tools, these requests to add or delete characters are coming up as (pending) and then coming up as failures like two minutes later. At the same time, the requests are working from the perspective of the application, meaning that if I add or delete characters as a user and then logout and log back in, the changes are still there. The register and login requests to the backend are working normally with statuses of 200 as well. What's happening here?
The backend:
const express = require('express');
const application = express();
const mongoose = require('mongoose');
application.use(express.json());
mongoose.connect('process.env.DATABASE_PASSWORD')
.then(console.log('Connected to database'));
const db = mongoose.connection;
const port = process.env.PORT || 8080;
application.post('/register', (request, response) => {
const username = request.body.username;
const password = request.body.password;
const favorites = [];
db.collection('data').insertOne({
username,
password,
favorites,
});
});
application.post('/login', async (request, response) => {
const username = request.body.username;
const password = request.body.password;
const findUser = await db.collection('data').findOne({
username,
password,
});
if (findUser) {
response.send({ message: 'Welcome, ' + username + "!", user: username, favorites: findUser.favorites });
} else {
response.send({ message: 'Login unsuccessful'});
}
});
application.post('/addFavorite', (request, response) => {
const userNow = request.body.username;
const favoritesHere = request.body.favoritesCopy;
console.log({userNow, favoritesHere});
db.collection('data').updateOne(
{ username: userNow },
{ $set: { favorites: favoritesHere }},
)
});
application.post('/deleteFavorite', (request, response) => {
const userNow = request.body.username;
const favoritesHere = request.body.theData;
db.collection('data').updateOne(
{ username: userNow },
{ $set: { favorites: favoritesHere }},
);
});
application.listen(port, () => {
console.log('Application listening');
});
The frontend fetch add request (the delete request is similar):
import React, { useState, useEffect } from 'react';
import logo from '../rickandmortylogo.png';
import { useSelector, useDispatch } from 'react-redux';
import { addFavorite } from '../index.js';
const Body = () => {
const [characters, setCharacters] = useState([]);
const [currentName, setCurrentName] = useState('Placeholder');
const [nameInput, setNameInput] = useState('');
const [locationInput, setLocationInput] = useState('');
const [loading, setLoading] = useState(true);
const favorites = useSelector(state => state.favoritesList);
const userNow = useSelector(state => state.currentUser);
const loggedIn = useSelector(state => state.loggedIn);
const dispatch = useDispatch();
useEffect(() => {
let isMounted = true;
let url = 'https://rickandmortyapi.com/api/character/';
let array = [];
const getData = async () => {
for (let i = 1; i < 4; i++) {
let response = await fetch(url);
let data = await response.json();
for (let j = 0; j < 20; j++) {
array.push(data.results[j]);
}
url = data.info.next;
}
if (isMounted) {
setCharacters(array);
setLoading(false);
}}
getData();
return () => {
isMounted = false;
}
}, []);
const readInput = (e) => {
setNameInput(e.target.value);
}
const readLocationInput = (e) => {
setLocationInput(e.target.value);
}
const addData = (a, b, c, d) => {
const array = [a, b, c, d];
const favoritesCopy = [...favorites];
favoritesCopy.push(array);
dispatch(addFavorite(array));
if (loggedIn === true) {
fetch('/addFavorite', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
favoritesCopy,
username: userNow,
}),
});
}
};
return (
<div className="pt-5">
<div className="text-center mt-5">
<img src={logo} className="img-fluid" />
</div>
<h2>Click on a character here to add them to your favorites. Choose "Check Favorites" in the menu bar to see your favorites and "Search Characters" to come back.</h2>
<div className="all">
<h4>Search by name:</h4>
<input onChange={readInput} />
<h4>Search by location:</h4>
<input onChange={readLocationInput} />
<br />
<div className="row m-1">
{loading ? 'Loading can take a few seconds. Your Rick and Morty experience will be ready soon!' : characters.filter((item) => {
if (nameInput == "") {
return item;
} else {
if (item.name.toLowerCase().includes(nameInput.toLowerCase())) {
return item;
}
}
}).filter((item) => {
if (locationInput == "") {
return item;
} else {
if (item.location.name.toLowerCase().includes(locationInput.toLowerCase())) {
return item;
}
}
}).map((item, id) => {
return (
<>
<div className="col-md-4 border border-dark rounded" id="square" onClick={() => addData(item.name, item.image, item.location.name, item.status)}>
<h2>{item.name}</h2>
<img src={item.image} className="border rounded" />
<h4>Location: {item.location.name}</h4>
<h4>Status: {item.status}</h4>
</div>
</>
)
})}
</div>
</div>
</div>
);
};
export default Body;
You never end the request. You don't send anything in the response and don't call response.end either, nor next. That's why your request never ends.
Here are some examples:
Success message with content
res.status(200).json({ success: true});
Success message without content
res.sendStatus(204);
Of course requests are pending, you never send anything on related actions:
Use res.send and send something, or at least in case of success, send a success status like:
204 for a no content success operation, like a DELETE for example.
201 for a POST operation creating a new resource.
5xx for errors
Related
I have a react component "Signin.js", I have some login input fields in it, I am trying to create a login system using nodejs, expressjs and MySQL. To do this, I have created a post request, that sends the data of my input fields to nodejs backend where the data is passed into the database to check if the login credentials are correct or not. If the credentials are found in the database, the global variable auth in my backend "index.js" becomes true, I am sending that variable from nodejs to reactjs using a get request. if auth is true, I should get redirected to the next page other wise it says, login failed.
The problem is that when I enter the correct credentials, in the first 2 clicks, I get login failed, but in the 3rd click, it redirects me to the next page.
Here is my react component Signin.js
import React, {useState , useEffect} from 'react';
import Navbar from './Navbar';
import './signin.css';
import {Link} from 'react-router-dom';
import axios, { Axios } from 'axios';
function Signin()
{
const [userId, setUserId] = useState("");
const [password, setPassword] = useState("");
const [accessCode, setAccessCode] = useState("");
const [auth , setAuth] = useState(false);
const receiveauthentication = () => {
console.log("Calling authentication function");
axios.get("http://localhost:3001/api/get/signin2").then((response) => {
setAuth(response.data);
console.log(response.data);
if(auth === true)
{
console.log(response.data);
setAuth(false);
document.location.href = "http://localhost:3000/Dashhome";
}
else{
alert('Incorrect Login/Password');
}
})
};
const signInInfoSent = () => {
axios.post("http://localhost:3001/api/get/signin", {userId:userId, password:password}).then(() => {
console.log("Successfully Login");
}).then(receiveauthentication());
}
return(
<>
<Navbar/>
<div id="signUp">
<div id="form">
<label htmlFor = "userId">User ID</label>
<input type="textbox" id="userId" placeholder = "Username" onChange={(e) =>{
setUserId(e.target.value);
}}/>
<label htmlFor = "password">Password</label>
<input type="password" id="password" placeholder = "Password" onChange={(e) =>{
setPassword(e.target.value);
}}/>
<label htmlFor = "otp">Access Code</label>
<input type="textbox" id="otp" placeholder = "OTP" onChange={(e) =>{
setAccessCode(e.target.value);
}}/>
<button onClick={()=>{ signInInfoSent() }}>Sign in</button>
</div>
</div>
</>
);
}
export default Signin;
Here is my nodejs backend index.js
const express = require('express')
const app = express()
const mySql = require('mysql');
const cors = require('cors')
const bodyParser = require('body-parser')
const db = mySql.createPool({
host:"127.0.0.1",
user:"root",
password:"password",
database:"kotekdatabase"
})
app.use(bodyParser.urlencoded({extended: true}))
app.use(express.json())
app.use(cors())
let auth;
var userIdG;
var trade_data = {};
app.post("/api/get/signin", (req,res)=>{
const userid = req.body.userId;
userIdG = userid;
const password = req.body.password;
const sqlSelect =
`SELECT * FROM signup WHERE userid = ? AND password = ?`;
db.query(sqlSelect,[userid , password], (err, result)=> {
//console.log(err);
if(result != null)
{
//res.send(result);
console.log("Successfully Login");
auth = true;
}
else
{
console.log("UnSuccessfully Login");
auth = false;
}
})
})
app.get("/api/get/retrieveStrategies" , (req , res)=>{
const q = 'SELECT * FROM strategy WHERE userid = ?';
db.query(q , [userIdG] , (err , result)=>{
if(err)
{
console.log(err);
}
else{
console.log(result[0]);
res.send(result);
}
});
})
app.get("/api/get/signin2", (req,res) =>{
console.log(auth);
res.send(auth);
auth = false;
})
var strategyG;
app.post("/api/get/strategy", (req,res)=>{
console.log("Strategy Name received\n");
const str = req.body.strategy;
strategyG = str;
console.log(strategyG);
})
var signupAuth = 0;
app.post("/api/insert", (req,res)=>{
const userid = req.body.userId;
const password = req.body.password;
const consumerKey = req.body.consumerKey;
const consumerSecret = req.body.consumerSecret;
const accessToken = req.body.accessToken;
const sqlInsert = "INSERT INTO signup (userid, password, consumerKey, consumerSecret, accessToken) VALUES (?,?,?,?,?);"
db.query(sqlInsert, [userid, password, consumerKey, consumerSecret, accessToken], (err, result)=> {
if(err){
console.log(err);
signupAuth = 0;
console.log(signupAuth);
}
else {
signupAuth = 1;
console.log(signupAuth);
}
})
})
app.post("/api/tradeinfo", (req,res)=>{
console.log("Inserting fo user Id,Strategy:", userIdG,strategyG);
const userid = userIdG;
const Sno = strategyG;
const indexName = req.body.indexName;
const Sprice = req.body.Sprice;
const SLP = req.body.SLP;
const PE = req.body.PE;
const Exitt = req.body.Exit;
const TType = req.body.TType;
const SL = req.body.SL;
const CE = req.body.CE;
const Entry = req.body.Entry;
const sqlInsert = "INSERT INTO strategy (userid, Sname, indexName, strikePrice, SLP, PE, Exitt, tradeType, SL, CE, Entry) VALUES (?,?,?,?,?,?,?,?,?,?,?);"
db.query(sqlInsert, [userid, Sno, indexName, Sprice, SLP, PE, Exitt, TType, SL, CE, Entry], (err, result)=> {
//console.log(err);
console.log(err);
})
})
app.post("/api/get/strategysent", (req,res)=>{
const userid = userIdG;
const Sno = strategyG;
const indexName = req.body.indexName;
const Sprice = req.body.Sprice;
const SLP = req.body.SLP;
const PE = req.body.PE;
const Exitt = req.body.Exit;
const TType = req.body.TType;
const SL = req.body.SL;
const CE = req.body.CE;
const Entry = req.body.Entry;
const sqlSelect =
`SELECT userid FROM signup WHERE userid = ${userid} AND password = ${password} AND consumerKey = ${consumerKey}`;
db.query(sqlSelect, (err, result)=> {
//console.log(err);
if(result != null)
{
//res.send(result);
console.log("Successfully Login");
auth = true;
}
else
{
console.log("UnSuccessfully Login");
auth = false;
}
})
})
app.post("/api/get/trade_data" , (req,res)=>{
res.send(req.body);
console.log(req.body);
trade_data = req.body;
})
app.get("/api/send/trade_data" , (req , res)=>{
res.send(trade_data);
} )
app.listen(3001, () => {
console.log("running on port 3001");
})
You have a floating promise, receiveauthentication is called too soon, you need to call it within the first .then handler.
Try this:
axios.post("http://localhost:3001/api/get/signin", {userId:userId, password:password}).then(() => {
console.log("Successfully Login");
receiveauthentication();
edit
you can merge the signin to remove extra axios request, i.e. return auth on the first request, and wrap state checking within useEffect, as it's a side effect:
server:
app.post("/api/get/signin", (req,res)=>{
const userid = req.body.userId;
userIdG = userid;
const password = req.body.password;
const sqlSelect =
`SELECT * FROM signup WHERE userid = ? AND password = ?`;
db.query(sqlSelect,[userid , password], (err, result)=> {
//console.log(err);
if(result != null)
{
//res.send(result);
console.log("Successfully Login");
auth = true;
}
else
{
console.log("UnSuccessfully Login");
auth = false;
}
res.send(auth);
})
})
client
// set 0 to skip on first render
const [auth, setAuth] = useState(0);
const signInInfoSent = () => {
axios.post("http://localhost:3001/api/get/signin", {
userId: userId,
password: password
}).then((response) => {
console.log("Successfully Login");
setAuth(response.data);
}).catch(err => console.error(err));
}
useEffect(() => {
console.log('check auth', auth);
if (auth === true) {
document.location.href = "http://localhost:3000/Dashhome";
} else if (auth === false) {
alert('Incorrect Login/Password');
}
}, [auth]);
I'm using ReactJS to build a blog app. I can use axios get, put, delete but NOT POST. Every time I post a new blog, it gives me server responded with a status of 500 (Internal Server Error).
I have been struggle with this issue for a week and couldn't figure out the reason. Thank you very much for your help! Let me know if you need additional information.
Here are my codes:
API
import axios from 'axios'
const baseUrl = `/api/blogs`
let token = null
const setToken = newToken => {
token = `bearer ${newToken}`
}
const getAll = () => {
const request = axios.get(baseUrl)
return request.then(response => response.data)
}
const create = async newBlog => {
const config = {headers: { Authorization: token }}
const response = await axios.post(baseUrl, newBlog, config)
return response.data
}
const update = async (id, newObject) => {
const request = axios.put(`${baseUrl}/${id}`, newObject)
const response = await request
return response.data
}
const remove= async (id) => {
const config = {headers: { Authorization: token }}
const request = axios.delete(`${baseUrl}/${id}`, config)
const response = await request
return response.data
}
const exportedObject = { getAll, create, update, setToken, remove }
export default exportedObject
Frontend App.js
import React, { useState, useEffect, useRef } from 'react'
import blogService from './services/blogs'
import loginService from './services/login'
import Blog from './components/Blog'
import LoginForm from './components/LoginForm'
import BlogForm from './components/BlogForm'
import Togglable from './components/Togglable'
import Notification from './components/Notification'
import axios from 'axios'
const App = () => {
const [blogs, setBlogs] = useState([])
const [username, setUsername] = useState('')
const [password, setPassword] = useState('')
const [user, setUser] = useState(null)
const [loginVisible, setLoginVisible] = useState(false)
const [notificationText, setNotificationText] = useState("")
const [notificationStyle, setNotificationStyle] = useState("notification")
const [Toggle, setToggle] = useState(false)
const BlogFormRef = useRef()
useEffect(() => {
const Data = async () => {
const initialBlogs = await blogService.getAll()
setBlogs( initialBlogs )
}
Data()
}, [])
useEffect(() => {
const loggedUserJSON = window.localStorage.getItem('loggedBlogUser')
if (loggedUserJSON) {
const user = JSON.parse(loggedUserJSON)
setUser(user)
blogService.setToken(user.token)
}
}, [])
const addBlog = async (blogObject) => {
BlogFormRef.current.toggleVisibility()
if (blogObject.title !== '' && blogObject.author !== '' && blogObject.url !== '') {
const newBlog = await blogService.create(blogObject)
setBlogs(blogs.concat(newBlog))
setNotificationStyle('notification')
setNotificationText(`A new blog ${blogObject.title} by ${blogObject.author} is added`)
setToggle(!Toggle)
setTimeout(() => {
setToggle(false)
}, 5000)
setBlogs('')
console.log(blogObject)
document.location.reload()
} else {
setNotificationStyle('Warning')
setNotificationText('You must fill all fields to create a blog')
setToggle(!Toggle)
setTimeout(() => {
setToggle(false)
}, 5000)
}
}
Backend
const blogsRouter = require('express').Router()
const Blog = require('../models/blog')
const User = require('../models/user')
const jwt = require('jsonwebtoken')
const middleware = require("../utils/middleware")
blogsRouter.get('/', async (request, response) => {
const blogs = await Blog.find({}).populate('user', { username: 1, name: 1 })
response.json(blogs)
})
blogsRouter.get('/:id', (request, response) => {
Blog.findById(request.params.id)
.then(blog => {
if (blog) {
response.json(blog)
} else {
response.status(404).end()
}
})
})
blogsRouter.post('/', middleware.userExtractor, async (request, response) => {
const body = request.body
const user = request.user
const decodedToken = jwt.verify(request.token, process.env.SECRET)
if (!decodedToken.id){
return response.status(401).json({error: 'token missing or invalid'})
}
if(body.title === undefined){
return response.status(400).send({
error: 'title is missing'
})
}
else if(body.author === undefined){
return response.status(400).send({
error: 'author is missing'
})
}
else if(body.url === undefined){
return response.status(400).send({
error: 'url is missing'
})
}
else{
const blog = new Blog({
title: body.title,
author: body.author,
url: body.url,
likes: body.likes,
user: user.id
})
const savedBlog = await blog.save()
//console.log(savedBlog)
//console.log(user)
user.blogs = user.blogs.concat(savedBlog.id)
await user.save()
const populatedBlog = await savedBlog.populate('user', { username: 1, name: 1 }).execPopulate()
response.status(200).json(populatedBlog.toJSON())
}
})
blogsRouter.delete('/:id', middleware.userExtractor, async (request, response) => {
const blog = await Blog.findByIdAndRemove(request.params.id)
const user = request.user
const decodedToken = jwt.verify(request.token, process.env.SECRET)
if(! request.token || !decodedToken.id){
return response.status(401).json({error:'token is missing or invalid'})
}
else if(blog.user.toString() === user.id.toString()){
await Blog.findByIdAndRemove(request.params.id)
response.status(204).end()
}
else{
return response.status(401).json({error:'cannot process deletion'})
}
})
blogsRouter.put('/:id', async (request, response) => {
const body = request.body
const blog = {
title: body.title,
author:body.author,
url: body.url,
likes: body.likes
}
await Blog.findByIdAndUpdate(request.params.id, blog, { new: true })
.then(updatedBlog => {
response.json(updatedBlog)
})
})
module.exports = blogsRouter
Mongoose
const mongoose = require('mongoose')
const blogSchema = new mongoose.Schema({
title: {type:String,
required: true},
author:{type:String,
required: true},
url: {type:String,
required: true},
likes: {type:Number,
default: 0},
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'}
})
blogSchema.set('toJSON', {
transform: (document, returnedObject) => {
returnedObject.id = returnedObject._id.toString()
delete returnedObject._id
delete returnedObject.__v
}
})
const Blog = mongoose.model('Blog', blogSchema)
module.exports = Blog
Additional information: terminate output, the info is actually POST just cannot render in the front
terminal output
I'm doing an API using React, SQL and Sequelize. I'm already finished but now I was asked to do my API request using JavaScript oriented object. The problem is, I don't really know how to do it, and I don't really understand my errors.
This is the class I am trying to do:
API class
class Api {
constructor(hostName, token) {
this.hostName = hostName
this.token = token
}
async getPost() {
return await fetch('api/post/')
.then(res => {
if (!res.ok) {
throw Error(res.statusText + "-" + res.url);
}
return res;
})
.then(post => {
console.log(post)
return post
})
.catch(err => {
console.log("Page non trouvée")
console.log(err)
})
}
}
export const apiRequest = new Api();
Axios config
import axios from 'axios';
class AxiosConfig {
constructor() {
this.axios = axios.create();
this.axios.defaults.baseURL = `${process.env.REACT_APP_API_URL}`;
this.axios.defaults.headers = {
'Content-Type': 'application/json',
};
//All request will wait 2 seconds before timeout
this.axios.defaults.timeout = 2000;
this.axios.defaults.withCredentials = true;
}
GET = async (url) => {
return await this.axios.get(`/${url}`);
}
POST = async (url, payload) => {
return await this.axios.post(`/${url}`, payload);
}
PUT = async (url, payload) => {
return await this.axios.put(`/${url}`, payload);
}
DELETE = async (url) => {
return await this.axios.delete(`/${url}`);
}
}
export const axiosInstance = new AxiosConfig();
HandlePost() is the function I'm tring to put in oriented object
NewPostForm
import React, { useContext, useEffect, useState } from 'react';
import { NavLink } from 'react-router-dom';
import { UserContext } from '../../UserContext';
import { apiRequest } from '../../utils/api';
import { axiosInstance } from '../../utils/AxiosConfig'
import landscape from './../../assets/icons/landscape.svg'
const NewPostForm = () => {
const uid = useContext(UserContext)
const [userPicture, setUserPicture] = useState('')
const [firstName, setFirstName] = useState('')
const [lastName, setLastName] = useState('')
const [message, setMessage] = useState('')
const [postPicture, setPostPicture] = useState('')
const [file, setFile] = useState('')
useEffect(() => {
const getUserInfo = async () => {
if (uid !== null) {
const userId = uid.userId
await axiosInstance.GET (`api/auth/${userId}`)
.then((res) => {
setFirstName(res.data.firstName)
setLastName(res.data.lastName)
setUserPicture(res.data.profile)
})
.catch((err) => {
console.log(err)
})
}
}
getUserInfo()
}, [uid, firstName, lastName])
console.log('parfait', axiosInstance.GET(`api/post`)) // old one, works perfectly
console.log('objectif', apiRequest.getPost()) // new one, not getting the object I need
// console.log(axiosInstance.GET(apiRequest.getPost`${uid.userId}`)) // error
const handlePost = async () => {
if (message || postPicture) {
const data = new FormData();
data.append("UserId", uid.userId);
data.append("content", message);
if (file) {
data.append("image", file);
}
try {
const res = await axiosInstance.POST(`api/post`, data);
console.log('File uploaded', res.data);
// window.location = '/'
} catch (err) {
console.error('Failed to upload file', err);
}
cancelPost()
} else {
alert("Veuillez entrer un message")
}
}
const handlePicture = (e) => {
setPostPicture(URL.createObjectURL(e.target.files[0]))
setFile(e.target.files[0])
}
const cancelPost = () => {
setMessage('')
setPostPicture('')
setFile('')
}
return (
<form className='post-container' >
<h2 className='h1'>Créer un post</h2>
<NavLink to="/profile">
<figure title='Profil utilisateur' className='new card-header'>
<img className='nav-profile' src={userPicture ? userPicture : "./images/img/profile.png"} width='50px' alt="profil de l'utilisateur" />
<h3 className='h2'>{firstName} {lastName}</h3>
</figure>
</NavLink>
<div className='post-form'>
<textarea
type="text"
name="message"
id="message"
cols="50"
rows="5"
placeholder="Quoi de neuf ?"
onChange={(e) => setMessage(e.target.value)}
value={message}
></textarea>
{postPicture && <img src={postPicture} alt="preview" className="img-preview" />}
</div>
<div className='footer-form'>
<div className='icon'>
<input
type="file"
id='file-upload'
name='file'
accept='.jpg, .jpeg, .png'
onChange={(e) => handlePicture(e)}
/>
<label className='file-input__label' htmlFor="file-upload">
<img className='svg' src={landscape} alt="upload icone paysage" />
Ajouter une l'image
</label>
</div>
<div className='new button-container'>
{message || postPicture ? (
<button className='new cancel-btn' onClick={(e) => cancelPost()}>Annuler</button>
) : null}
<button className='new validate-btn' onClick={(e) => handlePost()}>Envoyer</button>
</div>
</div>
</form>
);
};
export default NewPostForm;
My first console.log() returning the object that I need, when the second (supposed oriented object one) doesn't. I don't know what I'm doing wrong and the documentation doesn't help, I don't have the "good" questions.
Thanks
EDIT :
class Api {
constructor(hostName, token) {
this.hostName = hostName
this.token = token
}
async getPost() {
try {
const res = await fetch('api/post')
if (!res.ok) {
throw Error(res.statusText + "-" + res.url);
}
return await res.json();
} catch(err) {
console.log("Page non trouvée")
console.log(err)
}
}
}
export const apiRequest = new Api();
The problem is with the code that how you used fetch method. This is from the documentation.
The Response object, in turn, does not directly contain the actual JSON response body but is instead a representation of the entire HTTP response. So, to extract the JSON body content from the Response object, we use the json() method, which returns a second promise that resolves with the result of parsing the response body text as JSON.
You have to update your getPost method like below.
async getPost() {
return await fetch('api/post/').then(res => {
if (!res.ok) {
throw Error(res.statusText + "-" + res.url);
}
return res.json();
}).then(post => {
console.log(post)
return post
})
.catch(err => {
console.log("Page non trouvée")
console.log(err)
})
}
Check the documentation
You already using async and await so you don't need to use .then. The code can be updated like below.
async getPost() {
try {
const res = await fetch('api/post/')
if (!res.ok) {
throw Error(res.statusText + "-" + res.url);
}
return await res.json();
} catch(err) {
console.log("Page non trouvée")
console.log(err)
}
}
I have an app that uses axios, socket and chagstreams that keeps track of data in mongodb.
When the user clicks the like or dislike button it send a axios.post request and updates the database. Then changestream then picks up on the change and fires a socket.emit() to send the new data to the front. In the front the updated data is stored in a state hook.
Clientside.js
function Home() {
var { username } = useParams();
const { state } = useLocation();
if (state !== null) {
var { validation } = state;
}
const [SearchedData, setSearchedData] = useState();
const [MainData, setMainData] = useState();
const socket = io("ws://localhost:5000");
React.useEffect(() => {
socket.emit("content");
socket.on("get-data", (data) => {
setMainData(data);
});
}, []);
socket.on("Updated", (data) => {
if (JSON.stringify(MainData) !== JSON.stringify(data))
setMainData(data);
});
return (
<div className="App">
<NavBar
CurrentUser={username}
SearchedData={setSearchedData}
MainData = {MainData}
socket={socket}
validation={validation}
/>
{MainData !== undefined ? (
<Content
CurrentUser={username}
SearchedData={SearchedData}
MainData={MainData}
validation={validation}
></Content>
) : (
<Loading></Loading>
)}
</div>
);
}
route.js
router.post("/:username/like", (req,res) => {
const {username, id} = req.body
Comments.findByIdAndUpdate(id, {$set: {score: username}}, {upsert: true}, (err, d) => {
if(err){
res.sendStatus(400)
}
else{
res.sendStatus(200)
console.log(d['_id'] + " Liked")
}
})
})
router.post("/:username/dislike", (req,res) => {
const {username, id} = req.body
Comments.findByIdAndUpdate(id, {$pull: {score: username}}, (err, d) => {
if(err){
res.sendStatus(400)
}
else{
res.sendStatus(200)
console.log(d['_id'] + " DisLiked")
}
})
})
Server.js
const express = require("express");
const app = express();
const socket = require("socket.io");
const server = app.listen(process.env.Port, () => {
console.log("Socket listening " + process.env.Port);
});
const io = socket(server, {
cors: {
origin: "*",
credential: true,
},
});
mongoose
.connect(process.env.MongoDB, {
useNewUrlParser: true,
useUnifiedTopology: true,
})
.then(() => {
console.log("Database Connected");
})
.catch((err) => {
console.log(err);
});
io.on("connection", (socket) => {
const changeStream = Comments.watch();
changeStream
.on("change", (next) => {
Comments.find({}, (err, save) => {
socket.emit("Updated", save.reverse());
});
})
.on("error", err => {
console.log(err)
})
socket.on("content", (username) => {
var recievedData;
Comments.find({}, function (err, data) {
if (!err) {
recievedData = data;
socket.emit("get-data", recievedData.reverse());
}
});
});
socket.on("user-data", (username) => {
Users.find({ username: username }, (err, save) => {
if (!err) socket.emit("recieve-user-data", save);
});
});
})
Content.js
function Content(props) {
const [MainDisplay, setMainDisplay] = useState();
const [Recusers, setRecusers] = useState()
const [DeleteModal, setDeleteModal] = useState({delete: false, id: ""})
const [DeetsIndex, setDeetsIndex] = useState(-1)
const [EditModal, setEditModal] = useState({edit: false, id: ""})
const onLike = (e) => {
e.preventDefault();
const { id } = e.target.dataset;
const data = {
username: props.CurrentUser,
id: id,
};
axios
.post(process.env.REACT_APP_baseServerurl + "/user/:username/like", data)
};
const onDislike = (e) => {
e.preventDefault();
const { id } = e.target.dataset;
const data = {
username: props.CurrentUser,
id: id,
};
axios
.post(process.env.REACT_APP_baseServerurl + "/user/:username/dislike", data)
};
const DeetsList = (e) => {
const { id } = e.target.dataset
if(DeetsIndex == id)
setDeetsIndex(-1)
else{
setDeetsIndex(id)
}
}
const DeleteConfirmation = (id) => {
setDeleteModal(DeleteModal => ({
delete: true, id: id
}))
setDeetsIndex(-1)
}
const Edit = (id) => {
setEditModal(EditModal => ({
edit: true, id: id
}))
setDeetsIndex(-1)
}
React.useEffect(() => {
if (props.SearchedData !== undefined && props.setSearchedData !== "") {
if(props.SearchedData["users"].length > 0){
setRecusers(props.SearchedData["users"])
}
setMainDisplay(props.SearchedData['comments']);
}
else if(JSON.stringify(MainDisplay) !== JSON.stringify(props.MainData)) {
setMainDisplay(props.MainData);
}
});
return (
<div className="Comment-container">
{(Recusers === undefined || props.SearchedData === undefined || Recusers.length === 0 )? <></> : <DisplayUsers users={Recusers}></DisplayUsers>}
{(DeleteModal.delete)? <Delete DeleteModal={[DeleteModal,setDeleteModal]} ></Delete> : <></>}
{(EditModal.edit)? <EditContent EditModal={[EditModal, setEditModal]} ></EditContent> : <></>}
{MainDisplay !== undefined ? (
MainDisplay.map((data, index) => {
return (
<div className="Comments" id={data["_id"]}>
<div className="Likes">
<div className="like">
<svg
viewBox="0 0 32 32"
data-id={data["_id"]}
xmlns="http://www.w3.org/2000/svg"
onClick={onLike}
>
<path
data-id={data["_id"]}
d="M28,14H18V4c0-1.104-0.896-2-2-2s-2,0.896-2,2v10H4c-1.104,0-2,0.896-2,2s0.896,2,2,2h10v10c0,1.104,0.896,2,2,2 s2-0.896,2-2V18h10c1.104,0,2-0.896,2-2S29.104,14,28,14z"
/>
</svg>
</div>
<p id="score">{data["score"].length}</p>
<div className="dislike">
<svg
viewBox="0 0 24 24"
data-id={data["_id"]}
className="dislike"
xmlns="http://www.w3.org/2000/svg"
onClick={onDislike}
>
<path
data-id={data["_id"]}
d="M18,11H6c-1.104,0-2,0.896-2,2s0.896,2,2,2h12c1.104,0,2-0.896,2-2S19.104,11,18,11z"
/>
</svg>
</div>
</div>
<div className="Comment-header">
<div className="Comment-info">
<div className="inner-header">
<div>
<img alt="profile-pic" src={data["avatar"]}></img>
</div>
<p className="Username">{data["username"]}</p>
</div>
<p>{data["createdAt"]}</p>
</div>
<div className="content">
<p>{data["content"]}</p>
</div>
</div>
<div className="deets">
<img
className="deet-img"
data-id={index}
src={require("./images/deets.png")}
width="40px"
onClick={DeetsList}
></img>
{
(DeetsIndex == index)?
<ul className="deet-list" data-index={index}>
{(props.validation && props.CurrentUser === data["username"])?
<>
<li onClick={() => {DeleteConfirmation(data["_id"])}}>Delete</li>
<li onClick={() => {Edit(data["_id"])}}>Edit</li>
</>
:
<></>
}
<li onClick={() => {DeleteConfirmation(data["_id"])}}>Share</li>
</ul>
:
<></>
}
</div>
</div>
);
})
) : (
<Loading></Loading>
)}
</div>
);
}
Problem:
After a few requests or minutes (idle time) the console displays connection errors in loop and the web browser slows down.
Firefox can’t establish a connection to the server at ws://localhost:5000/socket.io/?EIO=4&transport=websocket&sid=-Y4oqgeERHo7ernXABEn.\
Tried to run the app in Chrome but it has the same slowing down problem without any errors.
In both the browsers if i check the network tab it shows pending requests.
Tried to insert some string from the front end. It works fine the first few times then it begins to slow down. So i guess the problem is some where in the routes part where it sends the request to store the data in the DB cause when i enter the data and check my collection on the mongodb website/ Compass there is no data of such it appears only when the request has been successfully completed. Switching it entirely to sockets also made no difference
I don't think the error is the main problem. It just that the browser slows down..
Update:
I think i solved the problem. It was initially triggering socket.on("Updated") multiple times and with each request it kept on increasing. So right now the performance seems normal. I just replaced socket.on("Updated") with socket.once("Updated")..
I want to implement button behavior Show more.
When I click the button, new objects must be loaded without rebooting.
I do const [data, setData] = useState(users); to initialize the first 10 users.
When I press the button, that to this array data, I add the following 10 users
var users = response.data;
for (var i=0; i < users.length; i++) {
data.push(users[i]);
}
setData(data);
But nothing is rendered in the browser.
how to do it correct?
const Cards = ({users, bottomUrl }) => {
const [data, setData] = useState(users);
const handleSubmit = e => {
e.preventDefault();
const page = bottomUrl.slice(-1);
const axios = require('axios');
const url = '/users';
axios.get(url, {
params: { page: page }
}, {headers: {'Content-Type': 'application/json'}})
.then(function (response) {
var users = response.data;
for (var i=0; i < users.length; i++) {
data.push(users[i]);
}
setData(data);
})
.catch(function (error) {
console.log(error);
})
.then(function () {
// always executed
});
};
return (
<div>
<div className="users-list">
{data.map((element, elIndex) => (
<UserCard
key={elIndex}
lastName={element.lastName}
firstName={element.firstName}
description={element.description}
/>
))}
</div>
<div className="users-page-button-container">
<a className="button" onClick={handleSubmit} href={bottomUrl}>Show more</a>
</div>
</div>
)
};
You are pushing on the state object data. You must use an intermediate variable and pass that to setData
You are manipulating the state directly, which is wrong. Please update the handleSubmit method as below.
const handleSubmit = e => {
e.preventDefault();
const page = bottomUrl.slice(-1);
const axios = require('axios');
const url = '/users';
axios.get(url, {
params: { page: page }
}, {headers: {'Content-Type': 'application/json'}})
.then(function (response) {
users = response.data;
setData([...data, ...users]); // You need to save the previous data along with the new data and pass it together to setData function. So here data = previous data + new users data
})
.catch(function (error) {
console.log(error);
})
.then(function () {
// always executed
});
};