How to verify the username in the url - node.js

I can just type any username in the url and the same component gets rendered with the logged-in user's posts. Like when I'm logged in as rick, I can see my feed as http://localhost:3000/profile/Rick/posts
but if I type the url with any string or number or anything, it still renders the component. Like http://localhost:3000/profile/xcxcxcxcx/posts, it still shows the same posts as Rick's feed.
I think I have to verify the username.
Here's my code.
UserProfile
import React, { Component } from "react"
import { getCurrentUser } from "../actions/userActions"
import { connect } from "react-redux"
import { Link } from "react-router-dom"
class UserProfile extends Component {
componentDidMount() {
const authToken = localStorage.getItem("authToken")
this.props.dispatch(getCurrentUser(authToken))
}
render() {
const { isIdentifyingToken, username, email } = this.props
return (
<div>
{isIdentifyingToken ? null : (
<div className="card">
<div className="card-content">
<div className="media">
<div className="media-left">
<figure className="image is-48x48">
<img
src="https://bulma.io/images/placeholders/96x96.png"
alt="Placeholder image"
/>
</figure>
</div>
<div className="media-content">
<p className="title is-4">{username}</p>
<p className="subtitle is-6">{email}</p>
</div>
</div>
<Link to={`/profile/${username}/posts`}>My Posts</Link>
<br></br>
</div>
</div>
)}
</div>
)
}
}
const mapStateToProps = state => {
return {
isIdentifyingToken: state.auth.isIdentifyingToken,
username: state.auth.user.username,
email: state.auth.user.email,
id: state.auth.user._id
}
}
export default connect(mapStateToProps)(UserProfile)
UserFeed
import React, { Component } from "react"
import { getUserPosts, getCurrentUser } from "../actions/userActions"
import { connect } from "react-redux"
import Cards from "./Cards"
class UserFeed extends Component {
componentDidMount() {
const authToken = localStorage.getItem("authToken")
if (authToken) {
this.props.dispatch(getCurrentUser(authToken))
if (this.props && this.props.userId) {
this.props.dispatch(getUserPosts(this.props.userId))
} else {
return null
}
}
}
render() {
const { isFetchingUserPosts, userPosts } = this.props
return isFetchingUserPosts ? (
<p>Fetching....</p>
) : (
<div>
{userPosts &&
userPosts.map((post) => {
return <Cards key={post._id} post={post} />
})}
</div>
)
}
}
const mapStateToPros = (state) => {
return {
isFetchingUserPosts: state.userPosts.isFetchingUserPosts,
userPosts: state.userPosts.userPosts,
userId: state.auth.user._id,
}
}
export default connect(mapStateToPros)(UserFeed)
Cards
import React, { Component } from "react"
import { connect } from "react-redux"
import { compose } from "redux"
import { withRouter } from "react-router-dom"
class Cards extends Component {
render() {
const {title, description } = this.props.post
return (
<div className="card">
<div className="card-content">
<div className="media">
<div className="media-left">
<figure className="image is-48x48">
<img
src="https://bulma.io/images/placeholders/96x96.png"
alt="Placeholder image"
/>
</figure>
</div>
<div className="media-content" style={{ border: "1px grey" }}>
<p className="title is-5">{title}</p>
<p className="content">{description}</p>
</div>
</div>
</div>
</div>
)
}
}
const mapStateToProps = state => {
return state
}
export default compose(withRouter, connect(mapStateToProps))(Cards)
I have a backend route which verifies the user like: (it just takes the decoded object out from the token which contains the userId and verifies the user)
identifyUser: async (req, res, next) => {
try {
const userId = req.user.userId
const user = await User.findOne({ _id: userId })
if (!user) {
return res.status(500).json( {error: "No user found "})
}
return res.status(200).json( { user })
} catch(error) {
return next(error)
}
}
I'm not sure how do I verify the username in the params:(
Edit: App.js
render() {
return (
<div>
<Router>
<Switch>
<Route exact path="/" component={LandingPage} />
<Route path="/register" component={RegistrationForm} />
<Route path="/login" component={LoginForm} />
<PrivateRoute path="/feed" component={Feed} />
<PrivateRoute path="/post/new" component={NewForm} />
<PrivateRoute path="/post/edit/:id" component={EditForm} />
<PrivateRoute exact path="/profile/:username" component={UserProfile} />
<PrivateRoute path="/profile/:username/posts" component={UserFeed} />
<Route component={NotFoundPage} />
</Switch>
</Router>
</div>
)
}

UserFeed is rendered with the component prop, so it receives the route props, match, location, and history. The match prop is the one you should be interested in.
this.props.match.params.username
UserFeed, however, needs to react to the match prop updating and refetch the posts. Here I believe I'm correctly assuming the path's username is the same as the user's userId property. (feel free to correct me if assumption is incorrect). This you'll need the componentDidUpdate lifecycle function.
componentDidUpdate(prevProps) {
const { match: { params: { username } } } = this.props;
const { match: { params: { username: prevUsername } } } = prevProps;
if (prevUsername !== username) {
// whatever other logic you need to validate user first
this.props.dispatch(getUserPosts(username));
}
}

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 redirect to another view in React

Im developing an app where I need to login and after click tha Login button I need to redirect to another view that is in another layout, I've tried using this.props.history.push("/Inicio") to redirect when the Login is succesful. In this case this is the path.
{
path: "/Inicio",
name: "Inicio",
component: Inicio,
layout: "/admin"
},
This is the entire code
import React, { Component } from "react";
import {
Grid,
Col
} from "react-bootstrap";
import { Card } from "components/Card/Card.jsx";
import { FormInputs } from "components/FormInputs/FormInputs.jsx";
import Button from "components/CustomButton/CustomButton.jsx";
class Login extends Component {
constructor(props) {
super(props)
this.state = {
users: [],
user: '',
pass: '',
msg: '',
apiResponse:''
}
this.logChange = this.logChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this)
}
handleSubmit(event) {
event.preventDefault()
var data = {
user: this.state.user,
pass: this.state.pass,
msg: this.state.msg,
apiResponse: this.state.apiResponse
}
console.log(data)
fetch("http://localhost:9000/log/Login", {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(data)
}).then(function(response) {
if (response.status >= 400) {
throw new Error("Bad response from server");
}
return response.json();
}).then((data) => {
if(data == "success"){
console.log(data)
this.setState({users: data});
this.props.history.push("/Inicio");
window.location.reload();
}
else{
if(data == 'El usuario o la contraseƱa no coinciden'){
this.setState({ apiResponse: data })
}
}
}).catch(function(err) {
console.log(err)
});
}
logChange(e) {
this.setState({[e.target.name]: e.target.value});
}
render() {
return (
<div className="content">
<p class="col-md-4"></p>
<Grid >
<Col md={5}>
<Card
title="Login"
content={
<form method='POST' onSubmit= {this.handleSubmit}>
<p class="col-md-2"></p>
<FormInputs
ncols={["col-md-7"]}
properties={[
{
label: "Usuario",
type: "text",
bsClass: "form-control",
placeholder: "Usuario",
maxlength: 20 ,
name: "user",
onChange: this.logChange
}
]}
/>
<p class="col-md-2"></p>
<FormInputs
ncols={["col-md-7"]}
properties={[
{
label: "ContraseƱa",
type: "password",
bsClass: "form-control",
placeholder: "ContraseƱa",
maxlength: 20,
name: "pass",
onChange: this.logChange
}
]}
/>
<p >{this.state.apiResponse}</p>
<br/>
<br/>
<Button bsStyle="info" pullRight fill type="submit">
Login
</Button>
<Button bsStyle="info" pullLeft fill type="submit">
Olvide mi ContraseƱa
</Button>
</form>
}
/>
</Col>
</Grid>
</div>
);
}
}
export default Login;
but in the handleSubmit() everything is working fine except for this.props.history.push("/Inicio") because it doesn't do anything.
The index.js code
import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter, Route, Switch, Redirect } from "react-router-dom";
import "bootstrap/dist/css/bootstrap.min.css";
import "./assets/css/animate.min.css";
import "./assets/sass/light-bootstrap-dashboard-react.scss?v=1.3.0";
import "./assets/css/demo.css";
import "./assets/css/pe-icon-7-stroke.css";
import AdminLayout from "layouts/Admin.jsx";
import LoginLayout from "layouts/LoginLayout.jsx";
import EfoodLayout from "layouts/EFoodLayout.jsx";
ReactDOM.render(
<BrowserRouter>
<Switch>
<Route path="/admin" render={props => <AdminLayout {...props} />} />
<Route path="/login" render={props => <LoginLayout {...props} />} />
<Route path="/Efood" render={props => <EfoodLayout {...props} />} />
<Redirect from="/" to="/login/Login" />
</Switch>
</BrowserRouter>,
document.getElementById("root")
);
LoginLayout
import React, { Component } from "react";
import { Route, Switch } from "react-router-dom";
import LoginNavbar from "components/Navbars/LoginNavbar";
import Footer from "components/Footer/Footer";
import routes from "routes.js";
class Login extends Component {
getRoutes = routes => {
return routes.map((prop, key) => {
if (prop.layout === "/login") {
return (
<Route
path={prop.layout + prop.path}
render={props => (
<prop.component
{...props}
handleClick={this.handleNotificationClick}
/>
)}
key={key}
/>
);
} else {
return null;
}
});
};
getBrandText = path => {
return "Bienvenido a E Food";
};
render() {
return (
<div className="wrapper">
<LoginNavbar
brandText={this.getBrandText(this.props.location.pathname)}
/>
<Switch>{this.getRoutes(routes)}</Switch>
<Footer />
</div>
);
}
}
export default Login;
I hope you can help me, and thanks to everyone who answer.
PD: If you need more from my code, please let me know.
Ok, since it's obvious the component you want to do the navigation in is a deeply nested component there are a couple options.
Prop Drilling (not recommended)
prop drilling
The cons of prop drilling is that if the right props aren't passed to begin with, or if any component along the way forgets to pass on props, then some child component won't receive what they need and it may be difficult to track down the what or why it's missing. It's more difficult to maintain as now any changes to this component necessitate needing to be concerned about every other component around it. Prop drilling is actually a react anti-pattern.
LoginLayout
Your LoginLayout component appears to consume a routes config object and dynamically render routes. It needs to pass on the route props it received from its parent. Any component between here and the Login where you want to use history.push would need to keep passing all the props down.
class Login extends Component {
getRoutes = routes => {
return routes.map((prop, key) => {
if (prop.layout === "/login") {
return (
<Route
path={prop.layout + prop.path}
render={props => (
<prop.component
{...props}
{...this.props} // <-- Pass on all props passed to this component
handleClick={this.handleNotificationClick}
/>
)}
key={key}
/>
);
} else {
return null;
}
});
};
...
render() {
return (
...
);
}
}
Use withRouter Higher Order Component (recommended)
withRouter
You can get access to the history objectā€™s properties and the closest
<Route>'s match via the withRouter higher-order component. withRouter
will pass updated match, location, and history props to the wrapped
component whenever it renders.
import React, { Component } from "react";
import { withRouter } from 'react-router-dom'; // <-- import HOC
import { Grid, Col } from "react-bootstrap";
import { Card } from "components/Card/Card.jsx";
import { FormInputs } from "components/FormInputs/FormInputs.jsx";
import Button from "components/CustomButton/CustomButton.jsx";
class Login extends Component {
constructor(props) {
...
}
handleSubmit(event) {
event.preventDefault();
var data = {
user: this.state.user,
pass: this.state.pass,
msg: this.state.msg,
apiResponse: this.state.apiResponse
};
console.log(data);
fetch("http://localhost:9000/log/Login", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data)
})
.then(function(response) {
if (response.status >= 400) {
throw new Error("Bad response from server");
}
return response.json();
})
.then(data => {
if (data == "success") {
console.log(data);
this.setState({ users: data });
this.props.history.push("/Inicio"); // <-- history prop should now exist
window.location.reload();
} else {
if (data == "El usuario o la contraseƱa no coinciden") {
this.setState({ apiResponse: data });
}
}
})
.catch(function(err) {
console.log(err);
});
}
logChange(e) {
this.setState({ [e.target.name]: e.target.value });
}
render() {
return (
<div className="content">
...
</div>
);
}
}
export default withRouter(Login); // <-- decorate Login with HOC
Are you using react-router-dom to handle your routing ? If so, you can use the hooks to navigate.
import { useHistory } from "react-router-dom";
...
const history = useHistory()
...
history.push('/Inicio')
Note that this will only work with React version >= 16.8
The reason why your code is not working is because of the bindings.
When using this.props.history.push("/Inicio"), this refers to the function you're in, not to the Component history.
If you want to keep your code that way, you can simply turn your function into an arrow function:
.then((data) => {
if(data == "success"){
console.log(data)
self.setState({users: data});
**this.props.history.push("/Inicio")** // now this will be correct
**window.location.reload();** // no need, react handles the reload
}
else{
if(data == 'El usuario o la contraseƱa no coinciden'){
self.setState({ apiResponse: data })
}
}
Although, I'd strongly advise using the latest features of react (check the hooks, it's really useful).

Recieving a "Cannot GET /login" error when navigating to route "http://127.0.0.1:8080/login" on my live server. "npm start" working fine though

I've built an app that is running well when on the server generated from the npm start command, but when I set up a server with npm live-server, or a python simple HTTPserver, I'm getting this error when I navigate to the /login route.
Saying "Cannot GET /login". But at no point do I send a get request from what I can tell. When I navigate to the /login route, all that should appear is a form that takes in user info and sends a post request to get a token.
Here are all of my user Actions in my UserActions.js file. As you can see it's very minimal (I don't even have a /register, this is because I only have 1 admin user already in the database and have no need to register)
usrerActions.js
import {
SET_USER,
SET_ERRORS,
CLEAR_ERRORS,
LOADING_UI,
SET_UNAUTHENTICATED
} from "../types";
import axios from 'axios'
export const loginUser = (userData, history) => (dispatch) => {
dispatch({ type: LOADING_UI });
axios
.post("/login", userData)
.then((res) => {
const FBIdToken = `Bearer ${res.data.token}`;
localStorage.setItem("FBIdToken", `Bearer ${res.data.token}`);
axios.defaults.headers.common['Authorization'] = FBIdToken;
// dispatch(getUserData());
dispatch({ type: CLEAR_ERRORS })
history.push("/");
})
.catch(err => {
dispatch({
type: SET_ERRORS,
payload: err.response.data
})
});
}
export const logoutUser = () => (dispatch) => {
localStorage.removeItem('FBIdToken');
delete axios.defaults.headers.common['Authorization'];
dispatch({ type: SET_UNAUTHENTICATED })
}
export const getUserData = () => (dispatch) => {
axios.get('/user')
.then(res => {
dispatch({
type: SET_USER,
payload: res.data
})
})
.catch(err => console.log(err));
}
My app.js with routes
...
const token = localStorage.FBIdToken;
if (token) {
const decodedToken = jwtDecode(token);
if (decodedToken.exp * 1000 < Date.now()) {
store.dispatch(logoutUser());
window.location.href = "/login";
} else {
store.dispatch({ type: SET_AUTHENTICATED });
axios.defaults.headers.common["Authorization"] = token;
}
}
function App() {
document.documentElement.classList.remove("nav-open");
React.useEffect(() => {
document.body.classList.add("index");
return function cleanup() {
document.body.classList.remove("index");
};
});
return (
// <MuiThemeProvider theme={theme}>
<Provider store={store}>
<Router>
<div className="main">
<Switch>
<Route exact path="/" component={home} />
<Route exact path="/products" component={products} />
<Route exact path="/retreats" component={retreats} />
<Route exact path="/tarot" component={tarot} />
<Route
path="/artist"
render={props => <ProfilePage {...props} />}
/>
<Route exact path="/login" component={login} />
</Switch>
</div>
<Footer />
</Router>
</Provider>
// </MuiThemeProvider>
);
}
export default App;
And my login.jsx page component
import React, { Component } from "react";
...
export class login extends Component {
constructor(){
super();
this.state = {
email: '',
password: '',
errors: {}
}
}
componentWillReceiveProps(nextProps) {
if(nextProps.UI.errors) {
this.setState({ errors: nextProps.UI.errors })
}
}
handleSubmit = (event) => {
event.preventDefault();
const userData = {
email: this.state.email,
password: this.state.password
}
this.props.loginUser(userData, this.props.history);
}
handleChange = (event) => {
this.setState({
[event.target.name]: event.target.value
})
}
render() {
// loading was before: { classes, UI: { loading } }
const { classes, loading } = this.props;
const { errors } = this.state;
return (
<>
<IndexNavbar />
<ProfilePageHeader />
<Grid container className={classes.form}>
<Grid item sm />
<Grid item sm>
<ChangeHistoryIcon />
<Typography variant="h3" className={classes.pageTitle}>
Login
</Typography>
<form noValidate onSubmit={this.handleSubmit}>
<TextField
id="email"
name="email"
type="email"
label="Email"
className={classes.textField}
helperText={errors.email}
error={errors.email ? true : false}
value={this.state.email}
onChange={this.handleChange}
fullWidth
/>
<TextField
id="password"
name="password"
type="password"
label="Password"
className={classes.textField}
helperText={errors.password}
error={errors.password ? true : false}
value={this.state.password}
onChange={this.handleChange}
fullWidth
/>
{errors.general && (
<Typography variant="body2" className={classes.customError}>
{errors.general}
</Typography>
)}
<Button
type="submit"
variant="contained"
color="primary"
className={classes.button}
>
Login
{loading && (
<CircularProgress size={30} className={classes.progress} />
)}
</Button>
</form>
</Grid>
<Grid item sm />
</Grid>
</>
);
}
}
login.propTypes = {
classes: PropTypes.object.isRequired,
loginUser: PropTypes.func.isRequired,
user: PropTypes.object.isRequired,
UI: PropTypes.object.isRequired
}
const mapStateToProps = (state) => ({
user: state.user,
UI: state.UI
});
const mapActionsToProps = {
loginUser
}
export default connect(mapStateToProps, mapActionsToProps)(withStyles(styles)(login));
Does anybody know why I get this error when I'm obviously sending a post request?
any help is greatly appreciated!
ANDit was brought to my attention that it might be an issue with my backennd not rendering the index file? I'm not sure what to do here. this is my backend index file
const functions = require('firebase-functions');
const app = require('express')();
const FBAuth = require('./util/fbAuth')
const cors = require('cors');
app.use(cors());
const { getAllPosts, createOnePost, getThePost, deletePost, uploadImage } = require('./handlers/posts');
const { login } = require('./handlers/users');
// Posts Routes
app.get('/posts', getAllPosts);
app.get('/post/:postId', getThePost);
app.post("/post", FBAuth, createOnePost);
app.delete('/post/:postId', FBAuth, deletePost);
app.post('/post/:postId/image', FBAuth, uploadImage);
//TODO update post
// Login Route
app.post('/login', login)
exports.api = functions.https.onRequest(app)
So I assume you're using react router dom. Are you using < Link to='/login' >? or are you using another way that actually sends a get request such as an a tag?
React Router Dom needs you to use their components to navigate on the react side of things.

How to I prevent user from accessing the page of my react app directly by entering the URL, and redirect them back to login page

I am creating a react-app. It is a website to keep track of some storage data.
currently I am trying to protect the routes so that the users can't just access it by entering the url of the page and access it directly.
I have seen some tutorial that uses Protected routes but I do not know how to implement it.
My login.js
import React, { Component } from 'react'
import { login } from './UserFunctions'
class Login extends Component {
constructor() {
super()
this.state = {
email: '',
password: ''
}
this.onChange = this.onChange.bind(this)
this.onSubmit = this.onSubmit.bind(this)
}
onChange(e) {
this.setState({ [e.target.name]: e.target.value })
}
onSubmit(e) {
e.preventDefault()
const user = {
email: this.state.email,
password: this.state.password
}
login(user).then(res => {
if (res) {
this.props.history.push(`/profile`)
}
})
}
render(props) {
return (
<div className="container">
<div className="row">
<div className="col-md-6 mt-5 mx-auto">
<form noValidate onSubmit={this.onSubmit}>
<h1 className="h3 mb-3 font-weight-normal">Please sign in</h1>
<div className="form-group">
<label htmlFor="email">Email Address</label>
<input type="email"
className="form-control"
name="email"
placeholder="Enter Email"
value={this.state.email}
onChange={this.onChange}
/>
</div>
<div className="form-group">
<label htmlFor="password">Password</label>
<input type="password"
className="form-control"
name="password"
placeholder="Enter Password"
value={this.state.password}
onChange={this.onChange}
/>
</div>
<button type="submit"
className="btn btn-lg btn-primary btn-block">
Sign in
</button>
</form>
</div>
</div>
</div>
)
}
}
export default Login
My app.js
import React, { Component } from 'react';
import { BrowserRouter as Router, Route, Link, Redirect, withRouter } from 'react-router-dom'
import { Home } from './appcomponents/Home';
import { Stockin } from './appcomponents/Stockin';
import { Stockout } from './appcomponents/Stockout';
import { Asrsstock } from './appcomponents/ASRSstock';
import { Productinfo } from './appcomponents/Productinfo';
import { Productin } from './appcomponents/Productin';
import { Productout } from './appcomponents/Productout';
import { Stockreport } from './appcomponents/Stockreport';
import { Enrolling } from './appcomponents/Enrolling';
import { NoMatch } from './appcomponents/NoMatch';
//import { NavigationBar } from './components/NavigationBar';
import { Deleteuser } from './appcomponents/Deleteuser';
import decode from 'jwt-decode';
import Navigationbar from './components/Navbar'
import Landing from './components/Landing'
import Login from './components/Login'
import Register from './components/Register'
import Profile from './components/Profile'
function requireAuth(nextState, replace) {
if (!loggedIn()) {
replace({
pathname: '/login'
})
}
}
class App extends Component {
render() {
return (
<Router>
<div className="App">
<Navigationbar />
<Route exact path="/" component={Landing} />
<div className="container">
<Route exact path="/register" component={Register} />
<Route exact path="/login" component={Login} />
<Route exact path="/profile" component={Profile} />
<Route exact path="/Home" component={Home} />
<Route path="/Stockin" component={Stockin} />
<Route path="/Stockout" component={Stockout} />
<Route path="/Asrsstock" component={Asrsstock} />
<Route path="/Productinfo" component={Productinfo} />
<Route path="/Productin" component={Productin} />
<Route path="/Productout" component={Productout} />
<Route exact path="/Stockreport" component={Stockreport} onEnter={requireAuth} />
<Route path="/Enrolling" component={Enrolling} />
<Route path="/Deleteuser" component={Deleteuser} />
<Route exact path="/Register" component={Register} />
<Route component={NoMatch} />
</div>
</div>
</Router>
);
}
}
export default App;
I want to be able to redirect unauthorized user to login page. So that the only way for them to access the protected pages is login in
/**
*
* PrivateRoutes
*
*/
import React from 'react';
import { Redirect, Route } from 'react-router-dom';
// Utils
const PrivateRoutes = ({ component: Component, ...rest }) => {
var session_token=localStorage.getItem('token')
return (
<Route {...rest} render={props => (
session_token !== null ? (
< Component {...props} />
) : (
<Redirect to={{
pathname: '/login',
state: { from: props.location }
}}
/>
)
)}
/>
)
};
export default PrivateRoutes;
Use this component to protect you routes
for example you can call your component like this
<PrivateRoutes exact path="/companies" name="Companies" component={props => <DefaultLayout {...props}/>} />
You can make a protected route like this example
You can use local storage for storing JWT, or for cookies , it's the same steps
1- In your App.js
import React, { Component } from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import Auth from './path/to/Auth';
import LogIn from './path/to/LogIn';
import NotProtected from './path/to/NotProtected';
import Protected from './path/to/Protected';
class App extends Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
return (
<BrowserRouter>
<div>
<Switch>
<Route exact path="/login" component={LogIn} />
<Route path="/not_protected" render={props => <NotProtected {...props} />} />
<Route path="/protected" render={props => <Auth><Protected {...props} /></Auth>} />
</Switch>
</div>
</BrowserRouter>
);
}
}
export default App;
2- In Auth.js
import React, { Component } from 'react';
import { withRouter } from 'react-router-dom';
import getJwt from './path/to/getJwt';
class Auth extends Component {
constructor(props) {
super(props);
this.state = {
Authenticated: false,
};
}
componentDidMount() {
const { history } = this.props;
const jwt = getJwt();
if (!jwt) {
history.push('/login');
} else {
this.setState({ Authenticated: true });
}
}
render() {
const { children } = this.props;
const { Authenticated } = this.state;
if (Authenticated === false) {
return (
<div>loading....</div>
);
}
return (
<div>
{children}
</div>
);
}
}
export default withRouter(Auth);
3- In getJWT Helper
const getJwt = () => {
return localStorage.getItem('jwt');
}
export default getJwt;
You can remove jwt from local storage on log_out
localStorage.removeItem('jwt')
App.js
class App extends Component {
// your logic for checking if user authorized to use protected route
// lets imagine that it just stored in local state for simplicity
state = {
isAuth: false
};
render () {
return (
<BrowserRouter>
<Switch>
<Route path="/not-protected" component={NotProtectedPage} />
{this.state.isAuth ? <Route path="/protected" component={ProtectedPage} /> : null}
<Route path="/" component={NotFoundError} />
</Switch>
</BrowserRouter>
);
}
}
If user is not authorized, the /protected route never would be rendered, therefore even if a user tries to access this route directly, it wouldn't be rendered and instead 404 will be rendered. If you want to show access denied page, change null in ternary operator to the same route, but with component that renders access denied.
you can store user token and detail in local storage .
login
localStorage.setItem("user", login_Api_Responce);
and set condition for authentication page like
rendar() {
let auth = localStorage.getItem('user');
return(
<React.Fragment>
{
!auth
?
this.props.history.push(`/login-page`);
:
<div>
//auth page
</div>
}
</React.Fragment>
);
}

After I submit my form the component does not update to render the new information

I have a component with a forum (redux-form) that adds a food to the data. After I click "ADD" the information I add in the form is added to the database, however, the component that renders the list of food does not update automatically unless I manually refresh the page. Then the new food is rendered in the component.
Not sure why this is not rendering the new information automatically
Stock.js (where the food list is rendered)
import _ from 'lodash';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { fetchFoodList, addToPot, deleteFood } from '../actions';
import Header from './header';
import Pot from './pot';
import AddFood from './add_food';
import RecipeList from './recipe_list';
import classes from "../index.css";
import { Row, Col } from 'react-flexbox-grid';
class FoodList extends Component {
componentDidMount () {
this.props.fetchFoodList();
}
addIngredientToPot = (ev) => {
const val = ev.target.dataset.value;
const newPot = [ ...this.props.pot,
val
];
this.props.addToPot(newPot)
}
onDeleteClick = (ev) =>{
// const {id} = this.props.match.params;
const val = ev.target.dataset.value;
this.props.deleteFood(val);
}
displayFoodList() {
return _.map(this.props.foods, food => {
return (
<li key={food._id} className={classes.tablerow}>
<div data-value={food.name}
onClick={this.addIngredientToPot.bind(this)}
className={classes.col1}>{food.name}
</div>
<div className={classes.col2}>{food.type}</div>
<div className={classes.col3}>
<button data-value={food._id}
onClick={this.onDeleteClick.bind(this)}
className={classes.throwoutbutton}
> Throw Out
</button>
</div>
</li>
);
});
}
render () {
// console.log(this.props.foods);
// console.log(this.props.pot)
return (
<div className={classes.stockpilecontainer}>
<Header />
<Row>
<Col lg >
<h2>StockPile</h2>
<ul className={classes.responsivetable}>
<li className={classes.tableheader}>
<div className={classes.col1}>Name</div>
<div className={classes.col2}>Type</div>
<div className={classes.col3}>Throw Out</div>
</li>
{this.displayFoodList()}
</ul>
<div>
<AddFood/>
</div>
</Col>
<Col/>
<Pot/>
<Col/>
</Row>
<Row>
<Col xs={12}>
<Row center="xs">
<RecipeList xs={6} />
</Row>
</Col>
</Row>
</div>
);
};
}
function mapStateToProps(state) {
return {
foods: state.foods,
pot: state.pot.pot,
};
}
export default connect (mapStateToProps, { fetchFoodList, addToPot, deleteFood })(FoodList);
Newfood.js (component with form to add a new food item)
import React, { Component } from 'react';
import { Field, reduxForm } from 'redux-form';
import { connect } from 'react-redux';
import { addFood } from '../actions';
import classes from "../index.css";
import { Redirect } from 'react-router-dom';
const renderField= field => {
const { input, type } = field;
return (
<div className={classes.addfoodinput}>
<label className={classes.addfoodlabel}>{field.label}</label>
<input {...input} type={type} className="form-control" />
</div>
)
}
class AddFood extends Component {
onSubmit(values) {
this.props.addFood(values);
}
render () {
const { handleSubmit } = this.props;
return (
<form onSubmit={handleSubmit(this.onSubmit.bind(this))} className={classes.addfoodform}>
<Field
label="Food "
name="name"
component={renderField}
/>
<Field
label="Type "
name="type"
component={renderField}
/>
<button type="submit" className={classes.addfoodbutton} >ADD</button>
<button className={classes.addfoodbutton}>Cancel</button>
{/* <Redirect to={'/stockpile'} /> */}
</form>
)
}
};
AddFood = reduxForm({
form: 'NewFoodForm',
fields: ['name', 'type'],
})(AddFood);
export default connect(null, {addFood})(AddFood)
Reducer for the food list:
import _ from 'lodash';
import { FETCH_FOODLIST } from '../actions/types';
export default function(state = {}, action) {
switch (action.type) {
case FETCH_FOODLIST:
return _.mapKeys(action.payload.data, '_id');
default:
return state;
}
}
Action creators for adding new food and get the food list:
// Action to get the food list:
export const fetchFoodList = () => dispatch => {
Axios
.get(`${ROOT_URL}/myfoodlist`, {
headers: { auth: localStorage.getItem('token') }
})
.then( response =>
dispatch({
type: FETCH_FOODLIST,
payload: response
}));
};
// Add a food to the DB:
export function addFood (values, history) {
return function (dispatch) {
Axios.post(`${ROOT_URL}/myfoodlist/foods`, values)
.then( response => {
dispatch ({
type: ADD_NEW_FOOD,
payload: response
});
// history.push('/');
history.pushState(null, '/stockpile');
})
.catch( () => {
});
};
}

Resources