Axios GET Request doesnt like undefined header(x-auth-token) - node.js

So I have a react app where the navbar component refuses to display after refreshing. The axios call to the endpoint doesnt appear to work if the header x-auth-token value is undefined.
I randomly set sethAuthtoken to a garbage value and it gives a 401 error, which makes sense, but if the localstorage.token has no value, I get nothing till it times out with the following message net::ERR_EMPTY_RESPONSE
This is action file, the navbar is referencing from:
Auth.js
import setAuthToken from '../utils/setAuthToken';
export const loadUser = () => async (dispatch) => { if
(localStorage.token) { setAuthToken(localStorage.token); }
const config = {
headers: {
"Content-Type": "application/json"
}
};
try {
const res = await axios.get("/api/auth", config);
dispatch({
type: USER_LOADED,
payload: res.data
});
} catch (err) {
dispatch({
type: AUTH_ERROR
});
}};
This is the back end route
router.get("/", auth, async (req, res) => {
try {
const user = await User.findById(req.user.id).select("-password");
res.json(user).status(200);
} catch (error) {
console.error(error.message);
res.status(500).send("Server error");
}});
This is the Navbar component code, which calls the action
import React, { Fragment } from "react";
import { Link } from "react-router-dom";
import { connect } from "react-redux";
import PropTypes from "prop-types";
import { logout } from "../../actions/auth";
const Navbar = ({ auth: { isAuthenticated, loading }, logout }) => {
const authLinks = (
<ul>
<li>
<a onClick={logout} href="!#">
<i className="fas fa-sign-out-alt" />{" "}
<span className="hide-sm">Logout</span>
</a>
</li>
</ul>
);
const guestLinks = (
<ul>
<li>
Developers
</li>
<li>
<Link to="/register">Register</Link>
</li>
<li>
<Link to="/login">Login</Link>
</li>
</ul>
);//will load guestlinks if not authenticated, authlinks if authenticated
return (
<nav className="navbar bg-dark">
<h1>
<Link to="/">
<i className="fas fa-code"></i> DevConnector
</Link>
</h1>
{loading ? null : (
<Fragment> {isAuthenticated ? authLinks : guestLinks} </Fragment>
)}
</nav>
);
};
Navbar.propTypes = {
logout: PropTypes.func.isRequired,
auth: PropTypes.object.isRequired
};
const mapStateToProps = (state) => ({
auth: state.auth
});
export default connect(
mapStateToProps,
{ logout }
)(Navbar);

I just put an else statement in the auth action file for it dispatch the AUTH_ERROR to the reducer, when theres no token in localstorage.
export const loadUser = () => async dispatch => {
if (localStorage.token) {
setAuthToken(localStorage.token);
} else {
dispatch({
type: AUTH_ERROR
});
}

Related

Cannot set headers after they are sent to the client [NEXTJS]

I am currently busy working on a personal project with a dashboard using NextJS, which I believe allows me to learn NextJS and the fundementals of typescript. I am trying to work out how to set a welcome message if a response sent back from the server is set to True, I did manage to code this in, but I got cannot set headers after they are sent to the client.
My entire code file for the dashboard is the following snippet:
import styles from "./index.module.css";
import { type NextPage } from "next";
import Head from "next/head";
import Link from "next/link";
import Container from 'react-bootstrap/Container';
import Nav from 'react-bootstrap/Nav';
import Navbar from 'react-bootstrap/Navbar';
import NavDropdown from 'react-bootstrap/NavDropdown';
import { signIn, signOut, useSession } from "next-auth/react";
import Image from 'react-bootstrap/Image'
import Router from "next/router";
import { useEffect, useState } from "react";
import { library } from '#fortawesome/fontawesome-svg-core'
import { faHouse, fas, faServer } from '#fortawesome/free-solid-svg-icons'
import "#fortawesome/fontawesome-svg-core/styles.css";
import { Offline, Online } from "react-detect-offline";
import { config } from "#fortawesome/fontawesome-svg-core";
// Tell Font Awesome to skip adding the CSS automatically
// since it's already imported above
config.autoAddCss = false;
{/* The following line can be included in your src/index.js or _App.js file*/}
import 'bootstrap/dist/css/bootstrap.min.css';
library.add(fas, faServer, faHouse)
// import the icons you need
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome'
const Protected: NextPage = () => {
const { status, data } = useSession()
const { data: sessionData2 } = useSession();
useEffect(() => {
if (status === "unauthenticated") Router.replace("/auth/signin");
}, [status]);
// const isBreakpoint = useMediaQuery(768)
// return (
// <div>
// { isBreakpoint ? (
// <div>
// <HamburgerMenu />
// </div>
// ) : (
// <div>
// <FullMenu />
// </div>
// )
if (status === "authenticated")
return (
<><Navbar bg="dark" expand="lg" variant="dark" className="justify-content-end flex-grow-1 pe-3">
<Container>
<Navbar.Brand href="#home" style={{ fontSize: '25px' }}><strong>Litika.</strong> </Navbar.Brand>
{/* <Navbar.Toggle aria-controls="basic-navbar-nav" /> */}
<Navbar.Collapse id="basic-navbar-nav" className={styles.rightNavbar}>
<Nav className={styles.rightNavbar}>
<Nav.Link href="#home" className={styles.body}><FontAwesomeIcon icon={faHouse} /></Nav.Link>
<Nav.Link href="#link" className={styles.body}>Link</Nav.Link>
<NavDropdown className={styles.body} title={<Image
src={sessionData2 ? `https://cdn.pixabay.com/photo/2015/10/05/22/37/blank-profile-picture-973460_640.png` : 'https://cdn.pixabay.com/photo/2015/10/05/22/37/blank-profile-picture-973460_640.png'}
roundedCircle
style={{ width: '30px' }} />}
id="basic-nav-dropdown">
<NavDropdown.Item href="#action/3.1" onClick={sessionData2 ? () => void signOut() : () => void signIn()}>
{sessionData2 ? "Sign out" : "Sign in"}
</NavDropdown.Item>
<NavDropdown.Item href="#action/3.2">
Another action
</NavDropdown.Item>
<NavDropdown.Item href="#action/3.3">Something</NavDropdown.Item>
<NavDropdown.Divider />
<NavDropdown.Item href="#action/3.4">
Separated link
</NavDropdown.Item>
</NavDropdown>
</Nav>
</Navbar.Collapse>
</Container>
</Navbar>
<div className={styles.connectivityStatus}>
<br></br>
<div className={styles.connectivityStatusBox}>
<Online>🟢 You are connected to the internet.</Online>
<Offline>🔴 You are not connected to the internet.</Offline>
</div>
<WelcomeContainer />
</div>
<main className={styles.main}>
<h1 className={styles.title}>
Litika. </h1>
<div className={styles.container}>
<div className={styles.cardRow}>
<div className={styles.card}>
<h1 className={styles.cardTitle}><FontAwesomeIcon icon={faServer} /> HELLO!</h1>
<p className={styles.cardText}>How are you? I am under the water.</p>
</div>
</div>
</div>
</main>
<main className={styles.main2}>
<div className={styles.container}>
</div>
</main>
</>
);
return (
<>
<main className={styles.main}>
<div className={styles.container}>
<h1 className={styles.title}>
Loading the Dashboard.
</h1>
</div>
</main>
<main className={styles.main2}>
<div className={styles.container}>
<h1 className={styles.title}>
</h1>
</div>
</main>
</>
);
};
export default Protected;
const WelcomeContainer: React.FC = () => {
const { data: sessionData } = useSession();
var [welcomeMessage, setWelcomeMessage] = useState(null);
const payload = JSON.stringify({
email: sessionData.user?.email,
});
fetch('/api/userService/isuserwelcome', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: payload
})
.then(response => response.json())
.then(data => {
console.log(data);
if (data === true) {
setWelcomeMessage(
<div className={styles.welcomeContainer}>
<h1>Welcome to the Los pollos hermanos family.</h1>
</div>
);
}
})
.catch(error => {
console.error(error);
});
return (
<>
{welcomeMessage}
</>
);
};
The code for the IsUserWelcome API Route is the following:
import type { NextApiRequest, NextApiResponse } from 'next';
import { PrismaClient } from '#prisma/client';
const prisma = new PrismaClient();
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
try {
const { email } = req.body;
const cliresponse = await prisma.user.findFirst({
where: {
email,
},
});
console.log(cliresponse.newUser)
if (cliresponse.newUser == true) {
res.status(200).json(cliresponse.newUser)
}
res.status(200).json({ cliresponse });
} catch (err) {
console.log(err)
res.status(500).json({ error: err});
}
}
I have identified the issue to be coming from the WelcomeContainer Functional Component and do understand that NextJS & React are dynamic, which will lead to updates through the DOM. However, I haven't really tried anything yet to fix this issue because nothing from doing a simple Google search could lead me to fix this issue, so any guidance & help will be appreciated!
The parts that are different in this question is that it pertains to react and NextJS in general, not express.
You are sending a response twice, after a response is send you should return from the function.
import type { NextApiRequest, NextApiResponse } from 'next';
import { PrismaClient } from '#prisma/client';
const prisma = new PrismaClient();
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
try {
const { email } = req.body;
const cliresponse = await prisma.user.findFirst({
where: {
email,
},
});
console.log(cliresponse.newUser)
if (cliresponse.newUser == true) {
res.status(200).json(cliresponse.newUser)
return; // you need to return here so the code below doesn't get executed
}
res.status(200).json({ cliresponse });
} catch (err) {
console.log(err)
res.status(500).json({ error: err});
}
}

How to get the object id after the button click in Reactjs

I am working in MERN project.
what I want
I am fetching the project list from the backend If i click on any project it should give the members list that is working on that project so i want to get the objectid of that clicked project
what i tried
import React, { useState, useEffect } from 'react'
import { NavLink } from 'react-router-dom'
import { useNavigate } from 'react-router-dom';
const AdminDash = () => {
const navigate = useNavigate()
const [userData, setuserData] = useState([])
const [data, setData] = useState({});
const callAboutPage = async () => {
try {
const res = await fetch("/alldata", {
method: "GET",
headers: {
Accept: "application/json",
"Content-Type": "application/json"
},
credentials: "include"
})
const data = await res.json()
setuserData(data)
console.log(setuserData);
if (!res.status === 200) {
const error = new Error(res.error)
throw error
}
} catch (error) {
console.log(error);
navigate("/")
}
}
function handleButtonClick(id) {
fetch(`/api/get-data/${id}`)
.then(response => response.json())
.then(data => {
setData(data);
console.log(data);
});
}
useEffect(() => {
callAboutPage()
}, [])
return (
<>
<div className='container mt-5'>
<div className='row'>
<div className='col-sm-10 col-md-10 col-lg-10'>
<div class="row align-items-md-stretch">
<div class="col-md-6">
<div class="h-100 p-5 text-bg-light rounded-3">
<h2>Current Ongoing Projects</h2>
<ol class="list-group list-group-numbered mt-5">
{
userData.map((item, i) => (
<li class="list-group-item d-flex justify-content-between align-items-start">
<div class="ms-2 me-auto">
<NavLink onClick={() => handleButtonClick()} to="/admindash" className="text-decoration-none"><div class="fw-bold">{item.name}</div></NavLink>
Content for list item
</div>
<i class="bi bi-at"></i>
</li>
))}
</ol>
<label>{data.name}</label>
</div>
</div>
</div>
</div>
</div>
</div>
</>
)
}
export default AdminDash
backend
app.get('/api/get-data/:id', (req, res) => {
ProjectSchema.findById(req.params.id, (err, doc) => {
if (err) {
res.send(err);
} else {
res.json(doc);
}
});
});
the output i get in my console
{stringValue: '"undefined"', valueType: 'string', kind: 'ObjectId', value: 'undefined', path: '_id', …}
How to achieve this ..any suggestions?
In the mapping onClick pass item.id.
First make sure you getting it in your data while Fetching.
<NavLink onClick={() => handleButtonClick(item.id)} to="/admindash" className="text-decoration-none">{item.name}

How to display error message on the mern app

I'm trying to display an error message when a user tries to sign in with an unregistered user but I'm not able to get that error message on the frontend(reactjs) that I am sending from my backend(nodejs, express, MongoDB).
I am using redux to handle the state of the react app.
user login form onSubmit code:
const onSubmit = (data) => {
if (isLogin) {
dispatch(signin(data, history));
} else {
dispatch(signup(data, history));
}
};
actions creators for auth
import * as api from "../api";
export const signin = (formData, history) => async (dispatch) => {
try {
// login the user
const { data } = await api.signIn(formData);
dispatch({ type: "AUTH", data });
history.push("/");
} catch (error) {
console.log(error);
dispatch({ type: "ERROR", data: error.response.data.message });
}
};
export const signup = (formData, history) => async (dispatch) => {
try {
// sign up the user
const { data } = await api.signUp(formData);
dispatch({ type: "AUTH", data });
history.push("/");
} catch (error) {
console.log(error);
}
};
reducers for auth:
const authReducer = (state = { authData: null }, action) => {
switch (action.type) {
case "AUTH":
localStorage.setItem("profile", JSON.stringify({ ...action?.data }));
return { state, authData: action?.data };
case "LOGOUT":
localStorage.clear();
return { state, authData: null };
case "ERROR":
return { state, authData: action?.data };
default:
return state;
}
};
export default authReducer;
Backend sign in code
const signin = async (req, res) => {
const { email, password } = req.body;
try {
const existingUser = await User.findOne({ email });
if (!existingUser)
return res.status(404).json({ message: "User doesn't exist." });
const isPasswordCorrect = await bcrypt.compare(
password,
existingUser.password
);
if (!isPasswordCorrect)
res.status(404).json({ message: "Invalid Credentials" });
const token = jwt.sign(
{ email: existingUser.email, id: existingUser._id },
"test",
{ expiresIn: "1h" }
);
res.status(200).json({ result: existingUser, token });
} catch (error) {
res.status(500).json({ message: "Something went wrong" });
}
};
sign API
import axios from "axios";
const API = axios.create({
baseURL: process.env.REACT_APP_BASE_URL,
});
export const signIn = (formData) => API.post("/user/signin", formData);
export const signUp = (formData) => API.post("/user/signup", formData);
Anyone, please help me with this.
Screenshots of the error response in Postman:
I just debugged the issue. First install redux-devtools-extension by npm install --save redux-devtools-extension
Then apply this in store(index.js file in your case) as follows
import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import { createStore, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import App from "./App";
import { reducers } from "./reducers";
import { composeWithDevTools } from "redux-devtools-extension";
const middleware = [thunk];
const store = createStore(
reducers,
composeWithDevTools(applyMiddleware(...middleware))
);
ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>,
document.getElementById("root")
);
And now you can use the error message anywhere as follows (your Auth.js file)
import React, { useState } from "react";
import { useSelector } from "react-redux";
import Input from "../utils/Input";
import Label from "../utils/Label";
import { useForm, FormProvider } from "react-hook-form";
import { GoogleLogin } from "react-google-login";
import { useDispatch } from "react-redux";
import { useHistory } from "react-router";
import { signin, signup } from "../../actions/auth";
const Auth = () => {
const [isLogin, setIsLogin] = useState(true);
const formMethods = useForm();
const auth = useSelector((state) => state.auth);
const { authData } = auth;
const { handleSubmit } = formMethods;
console.log(authData);
const dispatch = useDispatch();
const history = useHistory();
const changeScreen = () => {
setIsLogin(false);
dispatch({ type: "LOGOUT" });
};
const onSubmit = (data) => {
if (isLogin) {
dispatch(signin(data, history));
} else {
dispatch(signup(data, history));
}
};
const googleSuccess = async (res) => {
const result = res?.profileObj;
const token = res?.tokenId;
try {
dispatch({ type: "AUTH", data: { result, token } });
history.push("/");
} catch (error) {
console.log(error);
}
};
const googleFailure = (error) => {
console.log(error);
console.log("Google sign in was unsuccessful");
};
return (
<section className="col-start-1 col-end-2 md:col-start-2 md:col-end-3 row-start-2 row-end-3 md:row-start-1 md:row-end-2 mx-3 sm:mx-0 md:my-auto">
<div className=" w-full max-w-md bg-primaryOne px-6 py-8 rounded-md shadow-md mx-auto">
<FormProvider {...formMethods}>
<form className="" onSubmit={handleSubmit(onSubmit)}>
<div className="w-full flex justify-around mb-2">
<button
className={`${
isLogin
? "bg-secondaryTwo"
: "transition bg-transparent hover:bg-secondaryTwo"
} text-white text-xs font-bold px-6 py-4 rounded-full`}
type="button"
onClick={() => setIsLogin(true)}
>
LOG IN
</button>
<button
className={`${
!isLogin
? "bg-secondaryTwo"
: "transition bg-transparent hover:bg-secondaryTwo"
} text-white text-xs font-bold px-6 py-4 rounded-full`}
type="button"
onClick={() => changeScreen()}
>
SIGN UP
</button>
</div>
<div>
{!isLogin && (
<div>
<Label labelName="Name" />
<Input inputName="name" type="text" bgColor="primaryTwo" />
</div>
)}
<div>
<Label labelName="Email" />
<Input inputName="email" type="email" bgColor="primaryTwo" />
</div>
<div>
<Label labelName="Password" />
<Input
inputName="password"
type="password"
bgColor="primaryTwo"
/>
</div>
</div>
<div className="text-center">
<button
type="button"
onClick={() => setIsLogin(!isLogin)}
className="text-neutral font-extralight text-xs pt-6"
>
{authData && <h1 style={{ color: "red" }}>{authData}</h1>}
{!isLogin
? "Already have an account? Log In"
: "Don't have an account? Sign Up"}
</button>
</div>
<button className="bg-secondaryTwo hover:bg-secondaryOne transition px-4 py-3 w-full rounded-md text-white font-bold mt-4 shadow-md">
{isLogin ? "Log In" : "Sign Up"}
</button>
<div className="flex items-center py-6">
<div className="w-1/2 h-px bg-white bg-opacity-40"></div>
<p className="text-white px-1 text-xs">OR</p>
<div className="w-1/2 h-px bg-white bg-opacity-40"></div>
</div>
<div>
<GoogleLogin
clientId={process.env.REACT_APP_GOOGLE_CLIENT_ID}
onSuccess={googleSuccess}
onFailure={googleFailure}
cookiePolicy="single_host_origin"
render={(renderProps) => (
<button
className="bg-blue-600 hover:bg-blue-500 transition px-4 py-3 w-full rounded-md text-white font-bold mb-4 shadow-md"
type="button"
onClick={renderProps.onClick}
disabled={renderProps.disabled}
>
<i className="fab fa-google mr-2"></i>Continue with Google
</button>
)}
/>
</div>
</form>
</FormProvider>
</div>
</section>
);
};
export default Auth;
And one last thing. Check nested object before using its properties like error.response.data.message (your auth.js actions file)
import * as api from "../api";
export const signin = (formData, history) => async (dispatch) => {
try {
// login the user
const { data } = await api.signIn(formData);
dispatch({ type: "AUTH", data });
history.push("/");
} catch (error) {
console.log("An error occured while ");
const errMsg =
error.response && error.response.data.message
? error.response.data.message
: error.message;
dispatch({ type: "ERROR", data: errMsg });
console.log(errMsg);
}
};
export const signup = (formData, history) => async (dispatch) => {
try {
// sign up the user
const { data } = await api.signUp(formData);
dispatch({ type: "AUTH", data });
history.push("/");
} catch (error) {
console.log(error);
}
};
Hope it helps!
As far as I understood, everything is working fine, and actually, there is no error happening on the frontend side. You have to check the status code of "await api.signIn(formData);" response. If it is 200, it means that everything is ok otherwise you have to check the message in the response to get the message error. As on the frontend there is thrown error to be catched.
you can aslo use react-toastify . you can get status code and response message from error and store it in state object in reducers/auth.js and access it using useSelector() then create if else conditions according to status code and pass message to toast(#error_message#).
just see react-toastify and know how to use it.

I'm try to send a request to the server

But there is no request to the server. Is there any mistakes in this code?
import React, { useState, useEffect } from 'react';
import { Link } from 'react-router-dom'
import axios from 'axios';
import { useSelector, useDispatch } from 'react-redux';
import { listProducts } from '../actions/productActions';
function HomeScreen(props) {
const productList = useSelector(state => state.productList);
const { products, loading, error } = productList;
const dispatch = useDispatch();
useEffect(() => {
const fetchData = async () =>{
dispatch(listProducts());
}
return () =>{
//
};
}, [])
return loading ? <div>Loading...</div> :
error ? <div>{error}</div> :
<ul className="products">
{
products.map(product =>
<li key={product._id}>
<div className="product">
<Link to={'/product/' + product._id}>
<img className="product-image" src={product.image} alt="product"/>
</Link>
<div className="product-name">
<Link to= {'/product/' + product._id}> {product.name} </Link>
</div>
<div className="product-brand">{product.brand}</div>
<div className="product-price">Rs.{product.price}</div>
<div className="product-rating">{product.rating} Stars ({product.numReivews}Reviews)</div>
</div>
</li>)
}
</ul>
}
export default HomeScreen;
As you see in following screenshot, there is no request to the server.Does anyone have idea about this matter?
You're not calling the fetchData() function. Instead you've only declared it inside useEffect.
So instead of
useEffect(() => {
const fetchData = async () =>{
dispatch(listProducts());
};
return () =>{
//
};
}, [])
you can just write
useEffect(() => {
dispatch(listProducts());
return () =>{
//
};
}, [])
This might not be the full solution as, for example, I don't know what listProducts() does but certainly it is a part of the solution.

TypeError: Cannot read property 'name' of null in react

I am new to react and redux so I am not able to solve this bug easily after doing plenty of research.
I am trying to display profiles of users in a separate page in form of cards but I am getting an error:
TypeError: Cannot read property 'name' of null
ProfileItem.render
src/components/profiles/Profileitem.js:15
12 | <div className="row">
13 |
14 | <div className="col-lg-6 col-md-4 col-8">
> 15 | <h3>{profile.user.name}</h3>
16 | <p>
17 | {profile.status}{' '}
18 | {isEmpty(profile.company) ? null : (
â–¶ 22 stack frames were collapsed.
(anonymous function)
src/actions/userprofileAction.js:77
74 | dispatch(profileLoading());
75 | axios
76 | .get('/api/userprofile/all')
> 77 | .then(res =>
78 | dispatch({
79 | type: GET_PROFILES,
80 | payload: res.data
//this is the back-end of the userprofile
userprofile.js
const express = require('express');
const router = express.Router();
const mongoose=require('mongoose');
const passport=require('passport');
//loading validation after creating the userprofie valdiation
const validateProfileInput=require('../../validation/userprofile');
const validateExperienceInput=require('../../validation/experience');
//bring profile schema model
const Profile=require('../../models/Profile');
//bringing user schema model
const User=require('../../models/User');
//testing this api
router.get('/demo', (req, res) => res.json({ msg: 'Profile Works' }));
//get request
//now need to check for the user who is trying to login
router.get('/',passport.authenticate('jwt', { session: false }),(req, res) => {
//initializing this as empty because need to add error msg
const errors = {};
//fetch current user's profile and finding from profile model
//findone is a mongoose method which will find a specifc{single} thing
Profile.findOne({ user: req.user.id }) .populate('user', ['name']).then(profile => {
//if not found display error msg
if (!profile) {
errors.noprofile = 'no profile exists for this person';
return res.status(404).json(errors);
}
//if found then show proifle
res.json(profile);
})
.catch(err => res.status(404).json(err));
}
)
//to see all profiles at api/userprofile/all
router.get('/all',(req,res)=>{
Profile.find()
.populate('user',['name'])
.then(profiles=>{
if(!profiles){
errors.noprofile='no profiles';
return res.status(404).json(errors);
}
res.json(profiles);
})
.catch(err=>res.status(404).json({profile:'no profiles'}));
})
//getting profile by name
router.get('/profilename/:profilename',(req,res)=>{
Profile.findOne({profiename:req.params.profiename})
.populate('user',['name'])
.then(profile=>{
if(!profile){
errors.noprofile='there is no profile';
res.status(400).json(errors);
}
res.json(profile);
})
.catch(err=>res.status(404).json(err));
})
//getting profile by id
router.get('/users/:users_id',(req,res)=>{
Profile.findOne({profiename:req.params.profiename})
.populate('user',['name'])
.then(profile=>{
if(!profile){
errors.noprofile='there is no profile';
res.status(400).json(errors);
}
res.json(profile);
})
.catch(err=>res.status(404).json(err));
})
//post request
router.post(
'/',
passport.authenticate('jwt', { session: false }),
(req, res) => {
const {errors,isValid}=validateProfileInput(req.body);
//check validation
if(!isValid){
return res.status(400).json(errors);
}
//getting fields and adding in an obj
const fields={};
fields.user=req.user.id;
//checking if its sent from handle
if(req.body.profilename)fields.profilename=req.body.profilename;
if(req.body.company)fields.company=req.body.company;
if(req.body.location)fields.location=req.body.location;
//so splitting skills into an array when seperated by ','
if(typeof req.body.skills!=='undefined')
fields.skills=req.body.skills.split(',');
//searching by id and if profile has then update
Profile.findOne({user:req.user.id}).then(profile=>{
if(profile){
Profile.findOneAndUpdate({user:req.user.id},{$set:fields},{new:true})
.then(profile=>res.json(profile));
}
else{
//checking if there
Profile.findOne({profiename:fields.profilename}).then(profile=>{
if(profile){
errors.profiename='profile already there'
res.status(400).json(errors);
}
//saving and making new if not
new Profile(fields).save().then(profile=>res.json(profile));
})
}
})
}
);
//post req to add exp
router.post(
'/experience',
passport.authenticate('jwt', { session: false }),
(req, res) => {
const { errors, isValid } = validateExperienceInput(req.body);
// Check Validation
if (!isValid) {
// Return any errors with 400 status
return res.status(400).json(errors);
}
//to add new experience
Profile.findOne({ user: req.user.id }).then(profile => {
const newExperience = {
title: req.body.title,
company: req.body.company,
location: req.body.location,
from: req.body.from,
to: req.body.to,
description: req.body.description
};
// Add to exp array
profile.experience.unshift(newExperience);
profile.save().then(profile => res.json(profile));
});
}
);
//after adding if user wants to delete the experience
router.delete(
'/experience/:exp_id',
passport.authenticate('jwt', { session: false }),
(req, res) => {
const { errors, isValid } = validateExperienceInput(req.body);
// Check Validation
if (!isValid) {
// Return any errors with 400 status
return res.status(400).json(errors);
}
Profile.findOne({ user: req.user.id }).then(profile => {
const remove=profile.experience
.map(item=>item.id)
.indexof(req.params.exp_id);
//splicing out of array at index 1
profile.experience.splice(remove,1)
//saving
profile.save().then(profile=>res.json(profile));
})
.catch(err=>res.status(404).json(err));
}
);
module.exports = router;
userprofileaction.js
import axios from 'axios';
import {GET_USERPROFILE,PROFILE_LOADING,GET_ERRORS,CLEAR_CURRENT_PROFILE,GET_PROFILES} from './types';
//getting current profile
export const getProfile=()=>dispatch=>{
//dispatching loading state before req
dispatch(profileLoading());
axios.get('/api/userprofile')
.then(res=>
dispatch({
type:GET_USERPROFILE,
payload:res.data
}))
.catch(err=>
dispatch({
type:GET_USERPROFILE,
payload:{}
}))
}
// Create Profile
export const createProfile = (profileData, history) => dispatch => {
axios
.post('/api/userprofile', profileData)
.then(res => history.push('/dashboard'))
.catch(err =>
dispatch({
type: GET_ERRORS,
payload: err.response.data
})
);
};
export const addExp=(experienceData,history)=>dispatch=>{
axios.post('/api/userprofile/experience',experienceData)
.then(res=>history.push('/dashboard'))
.catch(err=>dispatch({
type:GET_ERRORS,
payload:err.response.data
}))
}
export const deleteExperience = id => dispatch => {
axios
.delete(`/api/userprofile/experience/${id}`)
.then(res =>
dispatch({
type: GET_USERPROFILE,
payload: res.data
})
)
.catch(err =>
dispatch({
type: GET_ERRORS,
payload: err.response.data
})
);
};
//loading the profile
export const profileLoading=()=>{
return{
type:PROFILE_LOADING
}
}
//clearing profile
export const clearcurrentprofile=()=>{
return{
type:CLEAR_CURRENT_PROFILE
}
}
//getting profiles
export const getProfiles = () => dispatch => {
dispatch(profileLoading());
axios
.get('/api/userprofile/all')
.then(res =>
dispatch({
type: GET_PROFILES,
payload: res.data
})
)
.catch(err =>
dispatch({
type: GET_PROFILES,
payload: null
})
);
};
profileItem.js
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Link } from 'react-router-dom';
import isEmpty from '../../validation/is-empty';
class ProfileItem extends Component {
render() {
const { profile } = this.props;
return (
<div className="card card-body bg-light mb-3">
<div className="row">
<div className="col-lg-6 col-md-4 col-8">
<h3>{profile.user.name}</h3>
<p>
{profile.status}{' '}
{isEmpty(profile.company) ? null : (
<span>at {profile.company}</span>
)}
</p>
<p>
{isEmpty(profile.location) ? null : (
<span>{profile.location}</span>
)}
</p>
<Link to={`/userprofile/${profile.profilename}`} className="btn btn-info">
View Profile
</Link>
</div>
<div className="col-md-4 d-none d-md-block">
<h4>Skill Set</h4>
<ul className="list-group">
{profile.skills.slice(0, 4).map((skill, index) => (
<li key={index} className="list-group-item">
<i className="fa fa-check pr-1" />
{skill}
</li>
))}
</ul>
</div>
</div>
</div>
);
}
}
ProfileItem.propTypes = {
profile: PropTypes.object.isRequired
};
export default ProfileItem;
Profiles.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import ProfileItem from './ProfileItem';
import { getProfiles } from '../../actions/userprofileAction';
class Profiles extends Component {
componentDidMount() {
this.props.getProfiles();
}
render() {
const { profiles, loading } = this.props.profile;
let profileItems;
if (profiles === null || loading) {
profileItems = <h3>Profiles are loading..........</h3>;
} else {
if (profiles.length > 0) {
profileItems = profiles.map(profile => (
<ProfileItem key={profile._id} profile={profile} />
));
} else {
profileItems = <h4>No profiles found.....</h4>;
}
}
return (
<div className="profiles">
<div className="container">
<div className="row">
<div className="col-md-12">
<h1 className="display-4 text-center">Job Seeker's profiles</h1>
<p className="lead text-center">
Explore and get in touch with a Job seeker for free!
</p>
{profileItems}
</div>
</div>
</div>
</div>
);
}
}
Profiles.propTypes = {
getProfiles: PropTypes.func.isRequired,
profile: PropTypes.object.isRequired
};
const mapStateToProps = state => ({
profile: state.profile
});
export default connect(mapStateToProps, { getProfiles })(Profiles);
userprofileReducer.js
import {GET_USERPROFILE,PROFILE_LOADING,CLEAR_CURRENT_PROFILE,GET_PROFILES} from '../actions/types';
//import { stat } from 'fs';
const initialstate={
//will have profile
profile:null,
profiles:null,
loading:false
}
export default function(state=initialstate, action){
switch(action.type){
case PROFILE_LOADING:
return{
...state,
loading:true
}
case GET_USERPROFILE:
return{
...state,
profile:action.payload,
loading:false
}
case GET_PROFILES:
return{
...state,
profiles:action.payload,
loading:false
}
case CLEAR_CURRENT_PROFILE:
return{
...state,
profile:null
}
default:
return state;
}
}
You first need to check profile whether it’s undefined or null and only then access its keys. You will be mostly using either ternary or && operator for such cases.
Below code should work for you
class ProfileItem extends Component {
render() {
const { profile } = this.props;
return (
<div className="card card-body bg-light mb-3">
<div className="row">
{profile && (
<div className="col-lg-6 col-md-4 col-8">
<h3>{profile.user && profile.user.name}</h3>
<p>
{profile.status}{' '}
{isEmpty(profile.company) ? null : (
<span>at {profile.company}</span>
)}
</p>
<p>
{isEmpty(profile.location) ? null : (
<span>{profile.location}</span>
)}
</p>
<Link to={`/userprofile/${profile.profilename}`} className="btn btn-info">
View Profile
</Link>
</div>
<div className="col-md-4 d-none d-md-block">
<h4>Skill Set</h4>
<ul className="list-group">
{profile.skills.slice(0, 4).map((skill, index) => (
<li key={index} className="list-group-item">
<i className="fa fa-check pr-1" />
{skill}
</li>
))}
</ul>
</div>
)}
</div>
</div>
);
}
}
ProfileItem.propTypes = {
profile: PropTypes.object.isRequired
};
export default ProfileItem;
It looks like you try to read properly before the value is set there. Try to add a checking before calling it. Something like:
if(profile.user !== 'null'){
...
}
Try to console.log(this.props) first thing on entering render.
Usually you get such errors when initial render is done and the values in props do not exist yet.
The solution will be to check this.props and this.props.xxx, and if they are null, display a 'loading' message / a spinner.
As the error highlights, the bug is at following line:
<h3>{profile.user.name}</h3>
You are trying to access name of object user but problem is the object is not as you expect. Its value is null hence it does not contain a property name. You need to debug where you are populating service response in user.
for me i have covered in try catch block and it works
let TOKEN =""
try{
TOKEN = JSON.parse(JSON.parse(localStorage.getItem("persist:root")).user).currentUser.accessToken ;
}
catch(err){
console.log(err);
}

Resources