React Socket NodeJs SocketManager not pulling through - node.js

Stuck in a problem, unable to understand why it is not working.
Everything seems okay.
Please advice.
This is Server.js
var app = module.exports.ie=require('http').createServer()
var io = require('socket.io')(app)
const PORT = process.env.PORT || 3231
const socketManager = require('./socketManager')
io.on('connection', socketManager)
app.listen(PORT, () =>
console.log(`Connected to Port ${PORT}`)
)
Below is the my Layout file which holds the main render.
import React, {Component} from 'react';
import io from 'socket.io-client';
import { USER_CONNECTED, LOGOUT } from './ConstEvents';
import ChatNameForm from './ChatNameForm';
const socketUrl = "http://localhost:3231"
export default class chatPane extends Component{
constructor(props){
super(props);
this.state ={
socket:null,
user: null
};
}
componentDidMount(){
this.initSocket()
}
/*
*
* Connect to 'socketUrl' and initialise the socket.
*
*/
initSocket =() =>{
const socket = io(socketUrl)
socket.on('connect', () =>{
console.log("Connected");
})
this.setState({socket})
}
/*
* Sets the user property in state
* #Param user {id:number, name:string}
*/
setUser = (user) =>{
const { socket } = this.state
socket.emit(USER_CONNECTED, user);
this.setState({user})
}
/**
* Sets the user property in state to Null.
*
*/
logout = () => {
const { socket } = this.state
socket.emit(LOGOUT);
this.setState({user:null})
}
render(){
const { socket } = this.state
return(
<div>
<ChatNameForm socket={socket} setUser={this.setUser} />
</div>
);
}
}
Below is the UserName form. No Passwords only setting up the user name here.
import React, {Component} from 'react';
import VERIFY_USER from './ConstEvents';
export default class ChatNameForm extends Component {
constructor(props){
super(props);
this.state = {
userChatName:"",
error:""
};
}
setUser = ({user, isUser}) => {
console.log(user, isUser);
if(isUser){
this.setError("User name Taken")
}
else {
this.props.setUser(user)
this.setError("")
}
}
handleSubmit = (e) => {
e.preventDefault()
const { socket } = this.props
const {userChatName} = this.state
socket.emit (VERIFY_USER, userChatName, this.setUser)
}
handleChange = (e) => {
this.setState({userChatName:e.target.value})
}
setError = (error) => {
this.setState({error})
}
render() {
const {userChatName, error} = this.state
return (
<div className='login'>
<form onSubmit = {this.handleSubmit} >
<label htmlFor= "userChatName">
<h2>Enter User Chat Name</h2>
</label>
<input
ref = {(input) => { this.textinput = input}}
type = "text"
id ="userChatName"
value = {userChatName}
onChange={this.handleChange}
placeholder={'userChatName'}
/>
<input type="submit" value= "ok" />
<div className= "error">
{error ? error:null}
</div>
</form>
</div>
)
}
}
And lastly My SocketManager..
const io = require('./Server.js').io
const { VERIFY_USER, USER_CONNECTED, LOGOUT } = require('../ConstEvents');
const { createUser, createMessage, createChat} = require('../ChatFunctions');
let connectedUsers = { }
module.exports = function(socket){
console.log(`Socket Id = ${socket.id}`)
// verify chatName
socket.on(VERIFY_USER, (userChatName, callback) => {
if (isUser (connectedUsers, userChatName)) {
callback ({ isUser:true, user:null })
} else{
callback ({ isUser:false, user:createUser({name:userChatName})})
}
})
//User connects with userName
socket.on(USER_CONNECTED, (user) => {
connectedUsers= addUser(connectedUsers, user)
socket.user = user
io.emit(USER_CONNECTED, connectedUsers)
})
}
function addUser (userList, user){
let newList = Object.assign({}, userList)
newList[user.name] = user
return newList
}
function removeUser(userList, username){
let newList = Object.assign({}, userList)
delete newList[username]
return newList
}
function isUser(userList, userChatName){
return userChatName in userList
}
Issue; I can get the socket working fine. Value from Form fine however I this values are not being pushed over to socketManager. I have done some tests in Form Js to see if the handleSubmit is working or not.. I can see socket, userChatName and socket.emit is working fine. however when I go to socket manager and look into the triggered Event in this case VERIFY_USER... I don't see any results after that. tried testing it but not sure how too get its value out on console to test. If you can help me out plz. Thank you.

Related

CURRENT_USER null, TypeError: Cannot read properties of null

I am facing an issue my user is always showing null in the backend, I don't know why.
I am following an Udemy course my tutor is saying middleware is responsible for user identification. but the problem is I typed the same code as my tutor is typing but his codes are working fine mine not.
I am using Axios from the front end to make request.
This my controller ==>
export const currentUser = async (req, res) => {
try {
const user = await User.findById(req._id).select("-password").exec();
console.log("CURRENT_USER", user); return res.json({ ok: true });
} catch (err) {
console.log(err);
}
};
This is my middleware ==>
import { expressjwt } from "express-jwt";
export const requireSignIn = expressjwt({ getToken: (req, res) => req.cookies.token, secret: process.env.JWT_SECRET, algorithms: ["HS256"], }) ;
This my front end code where I have been making request ===>
import { useEffect, useState, useContext } from "react";
import axios from "axios";
import { useRouter } from "next/router";
import { SyncOutlined } from "#ant-design/icons";
import UserNav from "../nav/userNav";
import { Context } from "../../context";
const UserRoutes = ({ children }) => {
const { state: { user } } = useContext(Context);
// state
const [ok, setOk] = useState(false);
// router
const router = useRouter();
useEffect(() => {
fetchUser();
}, []);
const fetchUser = async () => {
try {
const { data } = await axios.get("/api/current-user");
console.log(data);
if (data.ok) setOk(true);
} catch (err) {
console.log(err);
setOk(false);
router.push("/login");
}
};
return (
<>
{!ok ? (
<SyncOutlined spin className="d-flex justify-content-center display-1 text-primary p-5" />
) : (
<div className="UserNavSec">
<div className="UserNavCol1">
<div className="UserNavCont"><UserNav/></div
</div>
<div className="UserNavCol2"> {children} </div
</div>
)}
</>
);
};
export default UserRoutes;

react node js socket io emit does not reach the front

Hello I have a function to send to two sockets values
getMatchConfigurationFor = players => {
console.log(sessionMap.all())
if(players){
const match = new Match(players);
const result = {
idMatch: match.id,
playerOne: match.players[0],
playerTwo:match.players[1]
}
return result;
}
}
configurePlayersForNewMatch = (matchedPlayers) => {
matchedPlayers.forEach(player =>
sessionMap.get(player.socketId)
.broadcast.to(player.socketId)
.emit('match',
this.getMatchConfigurationFor(matchedPlayers)));
}
I tested all of it and it's working perfectly, but I tried to listen to the socket on the front end sme success I don't know why
my front end:
import React, { Component } from 'react';
import io from 'socket.io-client';
import Loading from './Loading'
import Players from './Players'
class Home extends Component {
constructor(props, context) {
super(props, context);
this.socket = null;
this.state = {
queue: [],
loading: true,
players: [],
};
}
componentDidMount() {
// io() not io.connect()
this.socket = io('http://localhost:9000');
const player = JSON.parse(localStorage.getItem('user'));
this.socket.emit('addPlayer-Queue', player);
this.socket.on('match', (result) => {
console.log('a')
console.log(result);
});
this.socket.open();
}
componentWillUnmount() {
this.socket.close();
}
render() {
const { queue } = this.state;
const { loading } = this.state;
const { players } = this.state
const visibility = loading ? 'hidden' : 'visible';
return (
<div className="container">
<div className="result">
</div>
<div className="ctnFlex">
<div className="playerOne">{players.map(pls => <p>{pls.name}</p>)}</div>
<Loading loading={loading} message='in queue.' />
<div className="playerTwo" style={{ visibility }}>
<Players players={players}/>
</div>
</div>
</div>
)
}
}
export default Home;
I have a session map of all sockets that come into my server:
var SessionMap = {};
module.exports = {
set: function(key,value){
SessionMap[key] = value;
},
get: function(key){
return SessionMap[key]
},
delete: function(key){
delete SessionMap[key]
},
all: function(){
return SessionMap
}
}
I debugged all my code and without any kind of error
A console. log of one of the sockets being sent the emit
https://pastebin.com/XHDXH9ih

items array is empty when trying to add items to cart

serverso I am trying to add items to the cart but the array is empty, I cant find the problem. I think the backend which is node.js is working correctly because when I add item to cart by typing the url in the browser http://localhost:4000/cart/5dd7668f33c21d811b74f403, it is increasing the cart quantity and redirecting to http://localhost:4000/cart/ but when I navigate to http://localhost:3000/cart/ there is nothing displayed in the browser and the items array is empty when I inspect it using chrome developers tools.I think the frontend react and the backend are not linked correctly. I have added the pictures of it for further clarification and here is the code of my application.
Server.js
const express = require("express");
const app = express();
const bodyParser = require("body-parser");
const cors = require("cors");
const cookieParser = require("cookie-parser");
const mongoose = require("mongoose");
let Book = require("./models/bookModel");
var session = require("express-session");
var MongoStore = require("connect-mongo")(session);
var flash = require("connect-flash");
const port = 4000;
app.use(cors());
app.use(bodyParser.json());
app.use(cookieParser());
app.use(
session({
secret: "keyboard cat",
resave: false,
saveUninitialized: true,
store: new MongoStore({ mongooseConnection: mongoose.connection }),
cookie: { maxAge: 180 * 60 * 1000 }
})
);
app.use(flash());
mongoose.connect("mongodb://127.0.0.1:27017/books", { useNewUrlParser: true });
const connection = mongoose.connection;
connection.once("open", function() {
console.log("MongoDB database connection established successfully..");
});
const bookRoutes = express.Router();
app.use("/books", bookRoutes);
const cartRoutes = express.Router();
app.use("/cart", cartRoutes);
bookRoutes.route("/").get(function(req, res) {
Book.find(function(err, books) {
if (err) {
console.log(err);
} else {
res.json(books);
}
});
});
bookRoutes.route("/:id").get(function(req, res) {
let id = req.params.id;
Book.findById(id, function(err, book) {
res.json(book);
});
});
cartRoutes.route("/").get(function(req, res) {
var cart = req.session.cart;
var displayCart = { items: [], total: 0 };
var total = 0;
for (var item in cart) {
displayCart.items.push(cart[item]);
total += cart[item].qty * cart[item].price;
}
displayCart.total = total;
return res.json(displayCart);
});
cartRoutes.route("/:id").get(function(req, res) {
req.session.cart = req.session.cart || {};
var cart = req.session.cart;
let id = req.params.id;
Book.findById(id, function(err, book) {
if (err) {
console.log(err);
}
if (cart[id]) {
cart[id].qty++;
} else {
cart[id] = {
item: book._id,
title: book.title,
price: book.price,
qty: 1
};
}
console.log(req.session.cart);
res.redirect("/cart");
});
});
app.listen(port, function() {
console.log("Server is running on Port: " + port);
});
App.js
import React, { Component } from "react";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
import BooksList from "./components/book-list.component.js";
import BookDetails from "./components/book-details.component.js";
import "bootstrap/dist/css/bootstrap.min.css";
import Navigation from "./components/navigation.component";
import Cart from "./components/cart1.component";
class App extends Component {
render() {
return (
<Router>
<Navigation></Navigation>
<Route
path="/"
exact
render={() => (
<div className="container">
<BooksList></BooksList>
</div>
)}
></Route>
<Route path="/books/:id" exact component={BookDetails}></Route>
<Route path="/cart" exact component={Cart}></Route>
</Router>
);
}
}
export default App;
book-details.componentjs
import React, { Component } from "react";
import "../css/styles.css";
import axios from "axios";
export default class BookDetails extends Component {
constructor(props) {
super(props);
this.state = {
book: [],
items: []
};
}
componentDidMount() {
axios
.get("http://localhost:4000/books/" + this.props.match.params.id)
.then(response => {
this.setState({ book: response.data });
})
.catch(function(err) {
console.log(err);
});
}
AddToCart = e => {
let id = e.currentTarget.getAttribute("id");
axios.get(`http://localhost:4000/cart/${id}`).then(() => {
window.location.href = "http://localhost:3000/cart";
});
};
render() {
const { book, quantity } = this.state;
return (
<div className="container">
<div className="row">
<div className="col sm-4">
<img src={`./images/${book.cover}`}></img>
</div>
<div className="col sm-8">
<h2>{book.title}</h2>
<ul>
<li>Category: {book.category}</li>
<li>Author: {book.author}</li>
</ul>
<p className="button blue">${book.price}</p>
<p>{book.description}</p>
<button id={book._id} onClick={this.AddToCart}>
Add to Cart
</button>
</div>
</div>
</div>
);
}
}
cart1.component.js
import React, { Component } from "react";
import axios from "axios";
import CartItem from "./cart1-item.component.js";
import "bootstrap/dist/css/bootstrap.min.css";
import { throws } from "assert";
export default class Cart extends Component {
constructor(props) {
super(props);
this.state = {
items: []
};
}
componentDidMount() {
axios
.get("http://localhost:4000/cart/")
.then(response => {
this.setState({
items: response.data.items
});
})
.catch(function(err) {
console.log(err);
});
}
checkItems() {
return this.state.items.map((currItem, i) => {
return <CartItem book={currItem} key={i}></CartItem>;
});
}
Calculate = item => {
return item.qty * item.price;
};
render() {
return (
<div className="container">
<div className="row">{this.checkItems()}</div>
</div>
);
}
}
cart-tem1.component.js
import React, { Component } from "react";
import "bootstrap/dist/css/bootstrap.min.css";
const CartItem = props => {
return (
<div className="container">
<h2>{props.book.title}</h2>
</div>
);
};
export default CartItem;
I have been stuck on this for almost a week now and tried all I could to make it work but to no avail, any help would be immensely appreciated
[chromedevelopertools][2]
It's hard to debug everything without trying it, but I'm pretty sure you have an issue with the session.
Sessions are implemented by cookies (things your browser sends on every request), and they depend on the domain for security reasons. They don't play well with Single Page Applications.
When you go to port :4000 the browser sends the cookies for you, but on port :3000, axios should send the cookies from that other domain. Try adding this config in your calls:
axios.get('xxx', {withCredentials: true});
It ~might work, though I feel you'll need to do some extra work somewhere else to initialize that cookie in the first place.
If you're going for the SPAs (which is popular choice nowadays) I'd recommend not using sessions and try to make your server 'Stateless'. It'll have other benefits too. Instead of that cookie thing, you'd need to implement something like Oauth or JWT (I recommend Jwt if you are getting started). That has enough keywords for you to google! :)

Cannot add to cart on frontend react.js, although the end points are working correctly and it is adding items to cart in post man app

so I am trying to add to cart. My node.js endpoints are working correctly and I am able to add items to cart when viewed in postman app but it does not display items on the front end, and when inspecting through the chrome developers tools, the items array is empty when on the postman while testing it is successfully storing items.
Here is my server.js
const express = require("express");
const app = express();
const bodyParser = require("body-parser");
const cors = require("cors");
const mongoose = require("mongoose");
let Book = require("./models/bookModel");
const port = 4000;
app.use(cors());
app.use(bodyParser.json());
mongoose.connect("mongodb://127.0.0.1:27017/books", { useNewUrlParser: true });
const connection = mongoose.connection;
connection.once("open", function() {
console.log("MongoDB database connection established successfully..");
});
const bookRoutes = express.Router();
app.use("/books", bookRoutes);
const cartRoutes = express.Router();
app.use("/cart", cartRoutes);
bookRoutes.route("/").get(function(req, res) {
Book.find(function(err, books) {
if (err) {
console.log(err);
} else {
res.json(books);
}
});
});
bookRoutes.route("/:id").get(function(req, res) {
let id = req.params.id;
Book.findById(id, function(err, book) {
res.json(book);
});
});
cartRoutes.route("/").get(function(req, res) {
var cart = req.session.cart;
var displayCart = { items: [], total: 0 };
var total = 0;
for (var item in cart) {
displayCart.items.push(cart[item]);
total += cart[item].qty * cart[item].price;
}
displayCart.total = total;
return res.json(displayCart);
});
cartRoutes.route("/:id").post(function(req, res) {
req.session.cart = req.session.cart || {};
var cart = req.session.cart;
let id = req.params.id;
Book.findById(id, function(err, book) {
if (err) {
console.log(err);
}
if (cart[id]) {
cart[id].qty++;
} else {
cart[id] = {
item: book._id,
title: book.title,
price: book.price,
qty: 1
};
}
res.redirect("/cart");
});
});
app.listen(port, function() {
console.log("Server is running on Port: " + port);
});
the server response:
{
"items": [
{
"item": "5dd7668f33c21d811b74f403",
"title": "Modern PHP",
"price": 25.65,
"qty": 1
},
{
"item": "5dd6bb36725bbba1ca482eea",
"title": "Professional Node.js",
"price": 20.56,
"qty": 2
}
],
"total": 66.77
}
cart.js
import React, { Component } from "react";
import axios from "axios";
import CartItem from "./cart1-item.component.js";
import "bootstrap/dist/css/bootstrap.min.css";
import { throws } from "assert";
export default class Cart extends Component {
constructor(props) {
super(props);
this.state = {
items: []
};
}
componentDidMount() {
axios
.get("http://localhost:4000/cart/")
.then(response => {
this.setState({
items: response.data.items
});
console.log(response.data.items);
})
.catch(function(err) {
console.log(err);
});
}
checkItems() {
return this.state.items.map((currItem, i) => {
return <CartItem book={currItem} key={i}></CartItem>;
});
}
Calculate = item => {
return item.qty * item.price;
};
render() {
return (
<div className="container">
<div className="row">{this.checkItems()}</div>
</div>
);
}
}
cartitem.js
import React, { Component } from "react";
import "bootstrap/dist/css/bootstrap.min.css";
const CartItem = props => {
return (
<div className="container">
<h2>{props.book.title}</h2>
</div>
);
};
export default CartItem;
here is the app.js code for cart route
<Route path="/cart" exact component={Cart}></Route>
Edited code book-details.component.js
import React, { Component } from "react";
import "../css/styles.css";
import axios from "axios";
export default class BookDetails extends Component {
constructor(props) {
super(props);
this.state = {
book: []
};
}
componentDidMount() {
axios
.get("http://localhost:4000/books/" + this.props.match.params.id)
.then(response => {
this.setState({ book: response.data });
})
.catch(function(err) {
console.log(err);
});
}
AddToCart = e => {
let id = e.currentTarget.getAttribute("id");
axios.post(`http://localhost:4000/cart/${id}`).then(() => {
window.location.href = "http://localhost:3000/cart/";
});
};
render() {
const { book, quantity } = this.state;
return (
<div className="container">
<div className="row">
<div className="col sm-4">
<img src={`./images/${book.cover}`}></img>
</div>
<div className="col sm-8">
<h2>{book.title}</h2>
<ul>
<li>Category: {book.category}</li>
<li>Author: {book.author}</li>
</ul>
<p className="button blue">${book.price}</p>
<p>{book.description}</p>
<button id={book._id} onClick={this.AddToCart}>
Add To Cart
</button>
</div>
</div>
</div>
);
}
}
App.js
import React, { Component } from "react";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
import BooksList from "./components/book-list.component.js";
import BookDetails from "./components/book-details.component.js";
import "bootstrap/dist/css/bootstrap.min.css";
import Navigation from "./components/navigation.component";
import Cart from "./components/cart1.component";
class App extends Component {
render() {
return (
<Router>
<Navigation></Navigation>
<Route
path="/"
exact
render={() => (
<div className="container">
<BooksList></BooksList>
</div>
)}
></Route>
<Route path="/books/:id" exact component={BookDetails}></Route>
<Route path="/cart/" exact component={Cart}></Route>
</Router>
);
}
}
export default App;
Any help would be appreciated.
i think i should point out that what you are passing to CartItem is "books" while in the CartItem component you are trying to get "items" from props (this.props.items). that part should be this.props.books.title.
UPDATE:
After you updated your question, i noticed this addition:
and on clicking the add to cart button it navigates to the link
href={"/cart"} className="button"> Add to Cart
this might be where your problem is coming from. on the API, to add books to cart, you did something like this:
cartRoutes.route("/:id").post(function(req, res) {
req.session.cart = req.session.cart || {};
var cart = req.session.cart;
let id = req.params.id;
so you are basically making a post request (even though from the code you are not really posting any data since you are just extracting the id from the url parameter. maybe you should consider making this a get request).
the key part here is the post http method and the id that is expected as the url parameter.
to make things simple on yourself, you can change your "add to cart" to something like:
<button className="button" id={book.id} onClick={this.addToCart}>
Add to Cart
</button>
for addToCart, you can do something like this:
addToCart=(e)=>{
let id = e.currentTarget.getAttribute("id");
axios.post(`http://localhost:4000/cart/${id}`)
.then(()=>{window.location.href = "http://localhost:3000/cart"})
}
note that like i said, you can replace the post request above to a get request since you are not actually posting any form data. if you wish to do this, you should also change the corresponding post request in your api to a get request.
Also, note that you can't get cart items posted through postman from the browser. you are using node sessions for cart items storage. you have to create a different react component (if you have not already created it) from where you can send the post request above to your express api

Fetch API - How Do I Send Query From Front End to Backend?

I need to pass a search query from my front end (in React) to my back end (Express) so that my Twitter API route will grab the correct data. Here is where I'm hitting the Twitter API. I was just playing around with a req.query to check out JSON so I know that part needs to be removed.
tweets.js
var express = require('express');
var router = express.Router();
const bodyParser = require('body-parser');
const Twit = require('twit');
const config = require('./config');
var T = new Twit(config);
/* GET users listing. */
router.get('/', function(req, res, next) {
let ticker = req.query.ticker;
T.get('search/tweets', { q: ticker })
.then(function(result) {
var tweets = result.data;
console.log(tweets);
res.send({tweets});
})
.catch(function(err) {
console.log('caught error', err.stack)
res.send({ error: err })
})
})
module.exports = router;
Also note route is set up like this in express
app.use('/tweets', tweets);
And here is my front end in React (ignoring the actual search component for now). Just confused as to how I would send a search query
import React, { Component } from 'react';
import '../App.css';
const filterData = (tweet) => {
return ((!tweet.retweeted) && !(tweet.text.includes('RT #') && ((tweet.in_reply_to_status_id) === null)));
};
class Twitter extends Component {
constructor(props) {
super(props);
this.state = {
tweets:[],
}
}
componentDidMount() {
this.getData("GOOG");
}
getData = (query) => {
fetch('/tweets?ticker='+query)
.then(res => res.json())
.then(data => data.statuses)
.then(statuses => statuses.filter(filterData))
.then(results => this.setState({tweets:results}))
}
render() {
return (
<div className="App">
<h1>Tweets About </h1>
{this.state.tweets.map(tweet =>
<div key={tweet.id}>{tweet.text}</div>
)}
</div>
);
}
}
export default Twitter;
The issue is here in this line.
Wrong one
fetch('/tweets?ticker={query)') <-- this is wrong
Correct one
const filterData = (tweet) => {
return ((!tweet.retweeted) && !(tweet.text.includes('RT #') && ((tweet.in_reply_to_status_id) === null)));
};
class Twitter extends Component {
constructor(props) {
super(props);
this.state = {
tweets:[],
}
}
componentDidMount() {
this.getData("GOOG");
}
getData = (query) => {
fetch(`http://localhost:3001/tweets?ticker=${query}`, {method: 'GET', headers: {"Content-Type": "application/json", "Access-Control-Allow-Origin": "*"}})
.then(res => res.json())
.then(data => data.statuses)
.then(statuses => statuses.filter(filterData))
.then(results => this.setState({tweets:results}))
}
renderTweets(){
if(this.state.tweets){
this.state.tweets.map(tweet =>
<div key={tweet.id}>{tweet.text}</div>
)
}
}
render() {
return (
<div className="App">
<h1>Tweets About </h1>
{this.state.tweets ? this.renderTweets(): ''}
</div>
);
}
}
export default Twitter;
Use template literals
fetch(`/tweets?ticker=${query}`)
OR
use normal string
fetch('/tweets?ticker='+query)
Check here for more details on how template literals works. Your code should be fine now.

Resources