How to fix problems with navigating to different pages by ID in a React app? - reactjs

I am having trouble navigating to different pages based on ID. I am making a recipe app with the MERN Stack.
I keep getting errors like No routes matched location "/api/recipes/61f9626a6b8d175f6b6dc725"
Here is my code:
App.js
import React from 'react';
import {BrowserRouter as Router, Route, NavLink, Routes} from 'react-router-dom';
import './App.css';
import Home from './components/pages/Home';
import RecipeList from './components/recipes/RecipeList';
import RecipeInfo from './components/recipes/RecipeInfo';
import RecipeAdd from './components/recipes/RecipeAdd';
import RecipeEdit from './components/recipes/RecipeEdit';
function App() {
return (
<div className="App">
<Router>
<Navigation />
<div className="container">
<Main />
</div>
</Router>
</div>
);
}
function Navigation() {
return(
<nav className="navbar navbar-expand-lg navbar-dark bg-dark mb-4">
<div className='container'>
<ul className="navbar-nav mr-auto">
<li className="nav-item"><NavLink exact="true" className="nav-link" to="/">Home</NavLink></li>
<li className="nav-item"><NavLink exact="true" className="nav-link" to="/recipes">Recipes</NavLink></li>
</ul>
</div>
</nav>
);
}
function Main() {
return(
<Routes>
<Route path="/recipes" element={<RecipeList />} />
<Route path="/recipes/new" element={<RecipeAdd />} />
<Route path="/recipes/:id" element={<RecipeInfo />} />
<Route path="/recipes/:id/edit" element={<RecipeEdit />} />
<Route exact path="/" element={<Home />} />
</Routes>
);
}
export default App;
RecipeInfo.js
import React, { useState, useEffect } from "react";
import axios from 'axios';
import { Link, useParams, useNavigate } from 'react-router-dom';
function RecipeInfo(props) {
const [recipe, setRecipe] = useState({});
const { _id } = useParams();
const navigate = useNavigate();
useEffect(function() {
async function getRecipe() {
try {
const response = await axios.get(`/api/recipes/${_id}/`);
setRecipe(response.data);
} catch(error) {
console.log('error', error);
}
}
getRecipe();
}, [props, _id]);
async function handleDelete() {
try {
await axios.delete(`/api/recipes/${_id}/`);
navigate("/api/recipes");
} catch(error) {
console.error(error);
}
}
return (
<div>
<h2>{recipe.title}</h2>
<small>_id: {recipe._id}</small>
<p>{recipe.ingredients}</p>
<p>{recipe.procedure}</p>
<div className="btn-group">
<Link to={`/api/recipes/${recipe._id}/edit`} className="btn btn-primary">Edit</Link>
<button onClick={handleDelete} className="btn btn-danger">Delete</button>
<Link to="/api/recipes" className="btn btn-secondary">Close</Link>
</div>
<hr/>
</div>
);
};
export default RecipeInfo;
Server.js
const express = require('express');
const mongoose = require('mongoose');
// const cors = require('cors');
const router = require('./routes/index');
const app = express();
const PORT = 3001;
const MONGODB_URI = "mongodb://localhost:27017/recipeDB";
// app.use(cors())
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
app.use('/api', router);
mongoose.connect(MONGODB_URI, {});
mongoose.connection.once('open', function() {
console.log('Connected to the Database.');
});
mongoose.connection.on('error', function(error) {
console.log('Mongoose Connection Error: ' + error);
});
app.listen(PORT, function() {
console.log(`Server listening on port ${PORT}.`);
});
I am also getting an error saying error TypeError: Cannot read properties of undefined (reading 'push') at postRecipe (RecipeAdd.js:23:1) Here is the code for that file.
RecipeAdd.js
import React, { useState } from "react";
import { post } from 'axios';
import { useNavigate } from "react-router-dom";
function RecipeAdd(props) {
const initialState = { title: '', content: '' }
const [recipe, setRecipe] = useState(initialState)
const navigate = useNavigate();
function handleChange(event) {
setRecipe({...recipe, [event.target.name]: event.target.value})
}
function handleSubmit(event) {
event.preventDefault();
if(!recipe.title || !recipe.ingredients || !recipe.procedure) return
async function postRecipe() {
try {
const response = await post('/api/recipes', recipe);
props.history.push(`/recipes/${response.data._id}`);
} catch(error) {
console.log('error', error);
}
}
postRecipe();
navigate("/recipes")
}
function handleCancel() {
navigate("/recipes");
}
return (
<div>
<h1>Create Recipe</h1>
<hr/>
<form onSubmit={handleSubmit}>
<div className="form-group">
<label>Title</label>
<input name="title" type="text" value={recipe.title} onChange={handleChange} className="form-control" />
</div>
<div className="form-group">
<label>Ingredients</label>
<textarea name="ingredients" rows="5" value={recipe.ingredients} onChange={handleChange} className="form-control" />
</div>
<div className="form-group">
<label>Procedure</label>
<textarea name="procedure" rows="5" value={recipe.procedure} onChange={handleChange} className="form-control" />
</div>
<div className="btn-group">
<input type="submit" value="Submit" className="btn btn-primary" />
<button type="button" onClick={handleCancel} className="btn btn-secondary">Cancel</button>
</div>
</form>
</div>
);
}
export default RecipeAdd;
I am fairly new to React, so any advice or best practices I could be doing would be greatly appreciated. Thanks!

Those errors seems to be valid.
Normal navigation around the app do not need to have /api/* before. Reason I think is because these are API routes and not navigation routes.
RecipeInfo.js
...
async function handleDelete() {
try {
await axios.delete(`/api/recipes/${_id}/`);
navigate("/recipes");
} catch(error) {
console.error(error);
}
}
...
return (
...
<Link to={`/recipes/${recipe._id}/edit`} className="btn btn-primary">Edit</Link>
<button onClick={handleDelete} className="btn btn-danger">Delete</button>
<Link to="/recipes" className="btn btn-secondary">Close</Link>
...
)
...
For the type error. Consider using navigate for routing instead of props being passed.
RecipeAdd.js line 23
// props.history.push(`/recipes/${response.data._id}`);
navigate(`/recipes/${response.data._id}`);

Related

When I change my context in an element it is not updated in other elemnts that use the same context

I am making a user context that has all of my user information that I need initially it is null and I update it in the login page but after I login and try to access the context from any other page it is still null
here is the code
CurrentUserContext.js
import { createContext } from "react";
export const CurrentUserContext = createContext(null);
UserContextLayout.jsx
import { Outlet } from "react-router-dom";
import { useState, useMemo, useCallback } from "react";
import { CurrentUserContext } from "../hooks/CurrentUserContext";
const UserContextLayout = () => {
const [currentUserContext, setCurrentUserContext] = useState(null);
return (
<CurrentUserContext.Provider
value={[currentUserContext, setCurrentUserContext]}
>
<Outlet />
</CurrentUserContext.Provider>
);
};
export default UserContextLayout;
MainRouter.jsx
import { Route, Routes } from "react-router-dom";
import Signin from "./pages/Signin";
import LandingPage from "./pages/LandingPage";
import { isAuthenticated } from "./apis/auth/auth-helper";
import UserContextLayout from "./utils/UserContextLayout";
const MainRouter = () => {
return (
<Routes>
<Route element={<UserContextLayout />}>
<Route path="/" element={<LandingPage />}></Route>
<Route
path="/signin"
element={
!isAuthenticated() ? <Signin /> : <Navigate to="/" replace={true} />
}
></Route>
</Route>
</Routes>
);
};
export default MainRouter;
Signin.jsx
import { useState, useContext } from "react";
import { GrTechnology } from "react-icons/gr";
import { CurrentUserContext } from "../hooks/CurrentUserContext";
import { signin } from "../apis/auth/auth-api";
import { authenticate } from "../apis/auth/auth-helper";
import { useNavigate } from "react-router-dom";
const Signin = () => {
const [currentUserContext, setCurrentUserContext] =
useContext(CurrentUserContext);
const navigate = useNavigate();
const [user1, setUser1] = useState({
email: "",
password: "",
});
const [rememberMe, setrememberMe] = useState(false);
const handleChange = (e) => {
setUser1((prev) => ({ ...prev, [e.target.name]: e.target.value }));
};
const handleRememberMeClick = () => {
setrememberMe((prev) => !prev);
};
const handleSubmit = async () => {
try {
const response = await signin(user1);
// if (res.status == 200) {
setCurrentUserContext(response.data.user);
authenticate(response.data.token, rememberMe);
navigate("/");
// } else {
// throw new Error("Authentication failed");
// }
} catch (error) {
console.log(error);
}
};
return (
<>
<div >
<div >
<h2 >
Sign in to your account
</h2>
<div >
<div>
<div>
<label htmlFor="email-address">
Email address
</label>
<input
id="email-address"
name="email"
type="email"
autoComplete="email"
required
onChange={handleChange}
/>
</div>
<div>
<label htmlFor="password">
Password
</label>
<input
id="password"
name="password"
type="password"
autoComplete="current-password"
required
onChange={handleChange}
/>
</div>
</div>
<div >
<div >
<input
id="remember-me"
name="remember-me"
type="checkbox"
onClick={handleRememberMeClick}
/>
<label
htmlFor="remember-me"
>
Remember me
</label>
</div>
</div>
<div>
<button
type="submit"
onClick={handleSubmit}
>
Sign in
</button>
</div>
</div>
</div>
</div>
</>
);
};
export default Signin;
response.data.user has the user data that I want to save in the context but the context is not updated when I call setCurrentUserContext

React router - click on card and redirect to a new page with content of that card (card details) using useParams and reacthooks

I have created a blog post. Posts are in the card[Event.js] and on click of the button. It should go to the new page and render its card details there. How can I do it using the react hooks and useParams.?
EventList.js ---> is where I'm fetching the data from api
Event.js ---> I'm rendering the fetched data in cards
EventDetails.js ---> It's the card details that should render on the screen when clicked on the post. Right now I have hard coded. the details.
Could someone please help me with how to do this?
//EventList.js
import React, { useState, useEffect } from "react";
import Event from "../Event/Event";
import axios from "axios";
import styles from "./EventList.module.css";
const EventList = () => {
const [posts, setPosts] = useState("");
let config = { Authorization: "..........................." };
const url = "............................................";
useEffect(() => {
AllPosts();
}, []);
const AllPosts = () => {
axios
.get(`${url}`, { headers: config })
.then((response) => {
const allPosts = response.data.articles;
console.log(response);
setPosts(allPosts);
})
.catch((error) => console.error(`Error: ${error}`));
};
return (
<div>
<Event className={styles.Posts} posts={posts} />
</div>
);
};
export default EventList;
//Event.js
import React from "react";
import styles from "./Event.module.css";
import { Link } from "react-router-dom";
import "bootstrap/dist/css/bootstrap.min.css";
const Event = (props) => {
const displayPosts = (props) => {
const { posts } = props;
if (posts.length > 0) {
return posts.map((post) => {
return (
<div>
<div>
<div className={styles.post}>
<img
src={post.urlToImage}
alt="covid"
width="100%"
className={styles.img}
/>
<div>
<h3 className={styles.title}>{post.title}</h3>
<div className={styles.price}> {post.author} </div>
<Link to={`/${post.title}`}>
<button className={styles.btns}> {post.author} </button>
</Link>
</div>
</div>
</div>
</div>
);
});
}
};
return <div className="Posts">{displayPosts(props)}</div>;
};
export default Event;
//EventDetails.js
import React, { useState, useEffect } from "react";
import Navbar from "../Navbar/Navbar";
import DetailsImage from "../../assets/Event-Ticketing.png";
import styles from "./EventDetails.module.css";
import "bootstrap/dist/css/bootstrap.min.css";
import { Link, useParams, useLocation } from "react-router-dom";
import axios from "axios";
// let config = { Authorization: "3055f8f90fa44bbe8bda05385a20690a" };
// const baseurl = "https://newsapi.org/v2/top-headlines?sources=bbc-news";
const EventDetails = (props) => {
const { state } = useLocation();
if (!state) return null;
// const [title, setTitle] = useState("");
// const { eventName } = useParams();
// useEffect(() => {
// axios
// .get(`${baseurl}`, { headers: config })
// .then((response) => setTitle(response.data));
// }, []);
// useEffect(() => {
// const neweventName = baseurl.find(
// (eventNames) => eventNames.eventName === parseInt(eventName)
// );
// setTitle(neweventName.title);
// }, []);
return (
<div>
<Navbar />
<div className={styles.eventBg}>
<div className="container">
<div>
<img
src={DetailsImage}
alt="ticket"
width="100%"
className={styles.heroEventImage}
/>
</div>
<div className={styles.bookingDetails}>
<div className={styles.nameBook}>
<div>
<div className={styles.eventNameHeader}>
<h1 className={styles.eventName}> {props.title}</h1>
</div>
<div className={styles.genre}>
<div className={styles.genreText}>{props.author}</div>
</div>
</div>
<div className={styles.bookingBtn}>
<div className={styles.booking}>
<Link to="/GeneralBooking">
<button
className={styles.bookBtn}
style={{ height: "60px", fontSize: "18px" }}
>
Book
</button>
</Link>
</div>
</div>
</div>
<div className={styles.venueTime}>
<div className={styles.dateTime}>
<div className={styles.dateTimeText}>{props.author}</div>
<div className={styles.price}>{props.author}</div>
</div>
</div>
</div>
</div>
</div>
</div>
);
};
export default EventDetails;
//App.js
import "./App.css";
import { BrowserRouter, Route, Switch } from "react-router-dom";
import Home from "./components/Home/Home";
import EventDetails from "./components/EventDetails/EventDetails";
import GeneralBooking from "./components/GeneralBooking/GeneralBooking";
import AllotedSeated from "./components/AllotedSeated/AllotedSeated";
import Checkout from "./components/Checkout/Checkout";
function App() {
return (
<BrowserRouter>
<div className="App">
<Switch>
<Route path="/" exact>
<Home />
</Route>
<Route path="/:title" exact children={<EventDetails />}></Route>
<Route path="/GeneralBooking" exact>
<GeneralBooking />
</Route>
</Switch>
{/* <EventDetails /> */}
{/* <GeneralBooking /> */}
{/* <AllotedSeated /> */}
{/* <Checkout /> */}
</div>
</BrowserRouter>
);
}
export default App;
Since it doesn't appear as though you've stored the posts state sufficiently high enough in the ReactTree to be accessible by component on other routes I suggest using route state to send a specific post object to a receiving route.
Event - Update the Link to send also the post object.
<Link
to={{
pathname: `/${post.title}`,
state: { post },
}}
>
<button type="button" className={styles.btns}>{post.author}</button>
</Link>
EventDetails - Use the useLocation hook to access the route state.
import { useLocation } from "react-router-dom";
const EventDetails = (props) => {
const { state } = useLocation();
if (!state.post) return null;
return (
// ...render all the post fields available from state.post
// i.e. state.post.title
);
};

Trying to authenticate and redirect to private route. Keeps redirecting/refreshing login page instead

I believe the issue is the isAuthenticated state in the parent APP component is not being changed when bcrypt has confirmed credentials in the child Login component. I can't seem to get it to change.
I have added some console.log to see if change takes place and console.log 2 is skipped over and is never read.
The examples I keep finding pass a value back to parent when it the return() "html" portion of the child component function. I tried to use that as a basis for passing a call to the funciton in the parent to change the state. As I am here that method does not seem to work. Any guidance and/or explanation would be greatly appreciated.
The ideal situation would be after being authenticated by bcrypt, user would be redirected to private route /Post
APP:
import React, {useState} from 'react';
import './App.css';
import Nav from "./Nav";
import About from './About';
import Creater from './Creater';
import {BrowserRouter as Router, Switch, Route, Redirect, Link } from 'react-router-dom';
import { Layout} from './components/Layout';
import Home from "./Home";
import Post from "./Post";
import Login from './Login';
function App() {
const [isAuthenticated, setIsAuthenticated] = useState (false)
console.log(isAuthenticated + "1");
function auth () { setIsAuthenticated (true);
console.log(isAuthenticated + "2");
}
console.log(isAuthenticated + "3");
const PrivateRoute = ({ component: Component, ...rest}) => (
<Route {...rest} render={(props)=>(
isAuthenticated === true
? <Component {...props}/>
: <Redirect to='/login' />
)}/>
)
return (<Router>
<div className="App">
<Nav />
<Layout>
<Switch>
<Route path="/" exact component={Home} />
<Route path="/about" component={About} />
<Route path="/creater" component={Creater} />
<Route auth={auth} path="/login" component={Login} />
<PrivateRoute isAuthenticated={isAuthenticated} path="/post" component={Post} />
</Switch>
</Layout>
</div>
</Router>
);
}
export default App;
Login:
import React, { useState } from "react";
import axios from 'axios';
var bcrypt = require('bcryptjs');
function Login({auth}) {
const [login, setLogin] = useState ({
adminname: "",
password: "",
});
function onChangeAdminname (event) {
setLogin({adminname: event.target.value,
password: login.password})
}
function onChangePassword (event) {
setLogin({adminname: login.adminname,
password: event.target.value})
}
function onSubmit ({event }) {
event.preventDefault();
const admin = login.adminname
const logpassword = login.password
axios.get('http://localhost:5000/admins', {
params: {
adminname: admin
}
})
.then(function (response) {
var pw = (response.data);
console.log(response.data);
bcrypt.compare(logpassword, pw[0].password, function(err, result){
if (result === true) {
console.log(auth);
auth();
window.location.pathname = "/post";
} else {
if(!alert("Admin name or password was incorrect, please try again")){window.location.reload();}
}
});
})
.catch(function (error) {
console.log(error);
});
}
return (
<div class="row">
<div class="col-sm-8">
<div class="card">
<div class="card-body">
<form
onSubmit={onSubmit}
>
<div class="form-group">
<input className="form-control"
type="text"
placeholder="Admin Name"
value={login.adminname}
onChange={onChangeAdminname}
name="adminmame"/>
</div>
<div class="form-group">
<input type="password"
className="form-control"
placeholder="Password"
value={login.password}
onChange={onChangePassword}
name="password"/>
</div>
<button value="POST" type="submit" className="btn btn-dark">Login</button>
</form>
</div>
</div>
</div>
</div>
)
}
export default Login;
post:
import React, { useState } from "react";
import axios from 'axios';
import {authenticated} from './Login';
import './App.css';
import DatePicker from 'react-datepicker';
import "react-datepicker/dist/react-datepicker.css";
function Post() {
const [postDate, setPostDate] = useState(new Date());
const [post, setPost] = useState({
title: "",
blog: "",
date: postDate,
});
function onChangeTitle(event) {
setPost({
title: event.target.value,
blog : post.blog,
date: post.date})
}
function onChangeBlog(event) {
setPost({
title: post.title,
blog : event.target.value,
date: post.date})
}
function onChangeDate(date) {
setPostDate(date);
}
function onSubmit(event) {
event.preventDefault();
axios.post('http://localhost:5000/posts/add', post)
.then(res => console.log(res.data));
window.location = '/';
}
return (
<div >
<h3>Compose a new post :D</h3>
<form onSubmit={onSubmit}>
<div className="form-group">
<input type="text"
required
className="form-control form-control-lg"
placeholder="Title"
value={post.title}
onChange={onChangeTitle}/>
</div>
<div>
<textarea type="text"
required
className="form-control form-control-lg"
rows="10"
placeholder="Tell a story"
value={post.blog}
onChange={onChangeBlog} ></textarea>
</div>
<div className="form-group">
<label>Date: </label>
<div>
<DatePicker
selected={postDate}
onChange={onChangeDate}/>
</div>
</div>
<div className="form-group">
<input type="submit" value="POST" className="btn btn-primary"/>
</div>
</form>
</div>
)
}
export default Post;
As far as I am concerned Bcrypt is not supported in ReactJS however can be used in NodeJS.
The browser supported version for Bcrypt is npm install bcryptjs
check their docs here:https://www.npmjs.com/package/bcryptjs

action is not a function react-redux

login undefined
I am getting login as undefined, whenever I click the login button it was working fine before I created AppRoutes.js and moved some of the routes into that file.
TypeError: login is not a function
here is the code structure.
this is the main file where app is started and login route is put here inside the AppRoutes component.
app.js
import React, { Fragment, useEffect } from 'react'
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'
import Navbar from './components/layout/Navbar'
import Landing from './components/layout/Landing'
import Alert from './components/layout/Alert'
import { loadUser } from './actions/auth'
import { Provider } from 'react-redux'
import setAuthToken from './utils/setAuthToken'
import store from './store'
import './App.css'
import AppRoutes from './components/routing/AppRoutes'
if (localStorage.token) {
setAuthToken(localStorage.token)
}
const App = () => {
useEffect(() => {
store.dispatch(loadUser())
}, [])
return (
<Provider store={store}>
<Router >
<Fragment>
<Navbar />
<Alert />
<Switch>
<Route exact path='/' component={Landing} />
<Route component={AppRoutes} />
</Switch>
</Fragment>
</Router>
</Provider>
)
}
export default App
2.AppRoutes.js
import React, { Fragment } from 'react'
import { Register } from '../auth/Register'
import { Login } from '../auth/Login'
import Dashboard from '../dashboard/Dashboard'
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'
import PrivateRoute from './PrivateRoute'
import { NotFound } from '../layout/NotFound'
import store from '../../store'
import { Provider } from 'react-redux'
const AppRoutes = () => {
return (<Fragment>
<Switch>
<Route exact path='/register' component={Register} />
<Route exact path='/login' component={Login} />
<PrivateRoute path='/dashboard' component={Dashboard} >
</PrivateRoute>
<Route component={NotFound} />
</Switch>
</Fragment>
)
}
export default AppRoutes
auth.js
export const login = (email, password) => async dispatch => {
const config = {
headers: {
'Content-Type': 'application/json'
}
}
const body = JSON.stringify({ email, password })
try {
const res = await axios.post('/api/auth', body, config)
if (res?.data?.token) {
dispatch({
type: LOGIN_SUCCESS,
payload: res.data
})
dispatch(loadUser())
}
else {
dispatch(setAlert(res.data.msg, 'success'))
}
} catch (error) {
const errors = error.response.data.errors
if (errors) {
errors.forEach(error => dispatch(setAlert(error.msg, 'danger')))
}
dispatch({
type: LOGIN_FAIL
})
}
}
4.Login.js
import React, { Fragment, useState } from 'react'
import { connect } from 'react-redux'
import PropTypes from 'prop-types'
import { login } from '../../actions/auth'
import { Link, Redirect } from 'react-router-dom'
import store from '../../store'
export const Login = ({ isAuthenticated, login }) => {
const [formData, setFormData] = useState({
email: '',
password: ''
})
const { email, password } = formData
const onChange = e => setFormData({ ...formData, [e.target.name]: e.target.value })
const onSubmit = e => {
e.preventDefault()
console.log(typeof login)
login(email, password)
}
//redirect if logged in
if (isAuthenticated) {
return <Redirect to='/dashboard' />
}
return (
<Fragment>
<div className="m-5">
<div className="row justify-content-center">
<div className="col-md-4">
<div className="card shadow-lg o-hidden border-0 my-5">
<div className="card-body p-0">
<div>
<div className="p-5">
<div className="text-center">
<h4 className="text-dark mb-4">Welcome Back!</h4>
</div>
<form className="user" onSubmit={e => onSubmit(e)}>
<div className="form-group">
<input className="form-control form-control-user" type="email" placeholder="Email Address" name="email" value={email} onChange={e => onChange(e)} required />
</div>
<div className="form-group">
<input className="form-control form-control-user" type="password"
placeholder="Password"
name="password"
minLength="6"
value={password} onChange={e => onChange(e)}
/>
</div>
<div className="form-group">
<div className="custom-control custom-checkbox small">
<div className="form-check">
<input className="form-check-input custom-control-input" type="checkbox" id="formCheck-1" />
<label className="form-check-label custom-control-label" htmlFor="formCheck-1">Remember Me</label>
</div>
</div>
</div><button className="btn btn-dark btn-block text-white btn-user" type="submit">Login</button>
<hr />
</form>
<div className="text-center"><Link to="/register" className="small" href="register.html">Create an Account!</Link></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</Fragment >
)
}
Login.propTypes = {
login: PropTypes.func.isRequired,
isAuthenticated: PropTypes.bool
}
const mapStateToProps = state => ({
isAuthenticated: state.auth.isAuthenticated
})
export default connect(mapStateToProps, { login })(Login)
I am not able to figure out whether it is related to the route structure that i changed or is there a bigger problem that I am missing out on.
You have imported Login a named import in AppRoutes, whereas the connected component with Login was a default export which is why you see the issue
Change
import { Login } from '../auth/Login'
to
import Login from '../auth/Login'

Uncaught Invariant Violation: Maximum update depth exceeded...etc. Error goes away on refreshing the page

This question seems like duplicate but it's not. I understand the error and have solved such error number of time but this time I couldn't figure out what's causing infinite loop.
here is code for react component causing the error:
import React, { Component } from 'react';
import { Redirect } from 'react-router-dom';
import PropTypes from 'prop-types';
import './Login.scss';
import axios from 'axios';
import { endPoint } from '../App/path';
import { logIn, isLoggedIn } from '../App/TokenStore';
const re = /^[a-zA-Z0-9_-]{3,15}$/;
class Login extends Component {
constructor() {
super();
this.state = {
username: '',
password: '',
errorMessage: 'Please Login to proceed!',
};
}
onUserNameChange = (e) => {
this.setState({ username: e.target.value });
}
onPasswordChange = (e) => {
this.setState({ password: e.target.value });
}
onFormSubmit = (event) => {
event.preventDefault();
const { username, password } = this.state;
const { history } = this.props;
const isUserNameValid = re.test(String(username).toLowerCase());
if (isUserNameValid) {
axios.post(`${endPoint}/login.json`, { username, password })
.then((response) => {
if (response.status === 200) {
if (response.statusText === 'OK') {
this.setState({
username: '', password: '', errorMessage: '',
});
if (response.config.data) {
console.log(response);
logIn(response.config.data, 'response.config.data.password');
history.push('/dashboard');
} else {
throw new Error();
}
} else {
this.setState({ errorMessage: 'Please enter valid credential!' });
}
}
}).catch((err) => {
if (err.response) {
this.setState({ errorMessage: 'Please enter valid username!' });
} else {
this.setState({ errorMessage: 'Unknown Error!!' });
}
});
} else {
this.setState({ errorMessage: 'Please enter valid username!' });
}
}
render() {
if (isLoggedIn()) {
return <Redirect to="/dashboard" />;
}
const {
username, password, errorMessage,
} = this.state;
return (
<div className="Login">
<form className="Form" onSubmit={this.onFormSubmit}>
<p className="errorMessage">{errorMessage}</p>
<div className="form__group">
<label id="username" className="Label" htmlFor="username">
<i className="fa fa-user" />
<b>Username</b>
</label>
<input
value={username}
onChange={e => this.onUserNameChange(e)}
id="username"
type="username"
placeholder="Your username"
required
/>
</div>
<div className="form__group">
<label id="password" className="Label" htmlFor="password">
<i className="fa fa-key" />
<b>Password</b>
</label>
<input
value={password}
onChange={e => this.onPasswordChange(e)}
id="password"
type="password"
placeholder="Your Password"
required
/>
</div>
<button type="submit" className="Btn">LOGIN</button>
</form>
</div>
);
}
}
Login.propTypes = {
history: PropTypes.shape(),
};
Login.defaultProps = { history: {} };
export default Login;
these are the other function I am using in the above component:
export const logIn = (username, token) => {
sessionStorage.setItem('username', username);
sessionStorage.setItem('loggedIn', true);
sessionStorage.setItem('token', token);
};
export const isLoggedIn = () => Boolean(sessionStorage.getItem('loggedIn'));
here is dashboard component
import React from 'react';
import { NavLink } from 'react-router-dom';
import './Dashboard.scss';
const dashboard = () => (
<div className="container">
<div className="row">
<div className="card col-5 mr-auto ml-auto">
<h5 className="card-header">Lorem ipsum</h5>
<div className="card-body">
<h5 className="card-title">Lorem ipsum</h5>
<p className="card-text">Lorem ipsum</p>
<NavLink to="/dashboard/VRlist" className="btn btn-dashboard">Update/Delete</NavLink>
</div>
</div>
<div className="card col-5 mr-auto ml-auto">
<h5 className="card-header">Lorem ipsum</h5>
<div className="card-body">
<h5 className="card-title">Lorem ipsum</h5>
<p className="card-text">Lorem ipsum</p>
<NavLink to="/dashboard/registration" className="btn btn-dashboard"> Registration</NavLink>
</div>
</div>
</div>
</div>
);
export default dashboard;
here is my main app component
import React from 'react';
import { Switch, Route } from 'react-router-dom';
import Navbar from './Navbar/Navbar';
import Login from './Login/Login';
import Dashboard from './Dashboard/Dashboard';
import { isLoggedIn } from './App/TokenStore';
import VRlist from './VRlist/VRlist';
import NewRegistration from './newRegistration/newRegistration';
import UpdateRegistration from './updateRegistration/updateRegistration';
const App = () => {
if (!isLoggedIn()) {
return (
<div>
<Navbar />
<Route component={Login} />
</div>
);
}
return (
<div>
<Navbar />
<Switch>
<Route exact path="/" component={Login} />
<Route path="/dashboard/VRlist/edit/:id" component={UpdateRegistration} />
<Route path="/dashboard/VRlist" component={VRlist} />
<Route path="/dashboard/registration" component={NewRegistration} />
<Route path="/dashboard" component={Dashboard} />
<Route component={Login} />
</Switch>
</div>
);
};
export default App;
Now when I run the application and do login, this error will happen only for the first time login in the current tab, if I refresh the page the error will go away.
Apparently, I've used react-router and react-router-dom version 5, that's what was causing an infinite render loop while calling an history.push(), however for the rest of the components 'history.push()' was working fine but login page. Though switching over to version 4.3.1 emitted issue completely.

Resources