I have a login page which has a forgot password link and it takes the user to forgot password page.
When I click on the forgot password link, it changes the URL but does not load the component.
Code for login page
import React, { useState, useEffect } from "react";
import { Link } from "react-router-dom";
// assets
import Logo from "../../../assets/images/kvh-logo.svg";
import bgImgArray from "../../../assets/images/bg";
import { Button, Form, Input, InputGroup, InputGroupAddon } from "reactstrap";
import "./Login.css";
const Login = (props) => {
const [loading, setLoading] = useState(false);
const userid = useFormInput("");
const password = useFormInput("");
const [error, setError] = useState(null);
// for changing backgrounds
const [index, setIndex] = useState(0);
return (
<div className="container-fluid backgroundContainer">
<div className="Login">
<div className="login-form-container">
<div className="logo">
<img src={Logo} className="App-logo" alt="logo" />
</div>
<div className="content">
<Form className="login-form">
<InputGroup>
<InputGroupAddon
className="input-group-addon"
addonType="prepend"
>
<i className="fa fa-user"></i>
</InputGroupAddon>
<Input
autoFocus
type="email"
aria-label="Username"
aria-describedby="username"
aria-invalid="false"
placeholder="Username or Email"
{...userid}
/>
</InputGroup>
<InputGroup>
<InputGroupAddon
className="input-group-addon"
addonType="prepend"
>
<i className="fa fa-lock"></i>
</InputGroupAddon>
<Input
type="password"
placeholder="Password"
aria-label="password"
aria-describedby="password"
{...password}
/>
</InputGroup>
<div className="form-actions">
{error && (
<>
<small style={{ color: "red" }}>{error}</small>
<br />
</>
)}
<br />
<Button
className="pull-right"
block="true"
type="submit"
bssize="small"
value={loading ? "Loading..." : "Login"}
onClick={handleLogin}
disabled={loading}
>
Login
</Button>
<br />
</div>
<div className="forgotPassword">
<Link to="/forgotPassword">Forgot password?</Link>
</div>
</Form>
</div>
</div>
</div>
</div>
);
};
const useFormInput = (initialValue) => {
const [value, setValue] = useState(initialValue);
const handleChange = (e) => {
setValue(e.target.value);
};
return {
value,
onChange: handleChange,
};
};
export default Login;
In routing code, I have Admin Layout which looks after the dashboard and AuthLayout which looks after the Login page.
I tried searching for the solution but unfortunately couldn't find any solutions. Hence, posting it here.
Router Code
import React from "react";
import {
BrowserRouter as Router,
Route,
Switch,
Redirect,
} from "react-router-dom";
import { createBrowserHistory } from "history";
import AdminLayout from "layouts/Admin/Admin.js";
import AuthLayout from "layouts/Auth/Auth.js";
import ResetPassword from "../components/pages/reset-password/ResetPassword";
const hist = createBrowserHistory();
const AppRouter = () => {
return (
<Router history={hist}>
<Switch>
<Route path="/admin" render={(props) => <AdminLayout {...props} />} />
<Route path="/" render={(props) => <AuthLayout {...props} />} />
<Route path="/forgotPassword" component={ResetPassword} />
<Redirect from="/" to="/auth" />
</Switch>
</Router>
);
};
export default AppRouter;
Adding Auth Layout Code
import React from "react";
import { Route, Switch, Redirect, Link } from "react-router-dom";
import Login from "../../components/pages/login/Login";
import ResetPassword from "../../components/pages/reset-password/ResetPassword";
import routes from "routes/routes.js";
class Pages extends React.Component {
getRoutes = (routes) => {
return routes.map((prop, key) => {
if (prop.collapse) {
return this.getRoutes(prop.views);
}
if (prop.layout === "/auth") {
return (
<Route
path={prop.layout + prop.path}
component={prop.component}
key={key}
/>
);
} else {
return null;
}
});
};
getActiveRoute = (routes) => {
let activeRoute = "WATCH";
for (let i = 0; i < routes.length; i++) {
if (routes[i].collapse) {
let collapseActiveRoute = this.getActiveRoute(routes[i].views);
if (collapseActiveRoute !== activeRoute) {
return collapseActiveRoute;
}
} else {
if (
window.location.pathname.indexOf(
routes[i].layout + routes[i].path
) !== -1
) {
return routes[i].name;
}
}
}
return activeRoute;
};
componentDidMount() {
document.documentElement.classList.remove("nav-open");
}
render() {
return (
<div className="wrapper wrapper-full-page" ref="fullPages">
<div className="full-page">
<Login {...this.props}></Login>
<div className="forgotPassword">
<Link to="/forgotPassword">Forgot password?</Link>
</div>
<Switch>
{this.getRoutes(routes)}
<Redirect from="*" to="/auth/login" />
</Switch>
</div>
</div>
);
}
}
export default Pages;
look at this code that you wrote:
<Route path="/" render={(props) => <AuthLayout {...props} />} />
<Route path="/forgotPassword" component={ResetPassword} />
it's never going to /forgotPassword because path always match with first Route.
you should use exact props:
<Route exact path="/" render={(props) => <AuthLayout {...props} />} />
<Route exact path="/forgotPassword" component={ResetPassword} />
If you create your own history then you should use Router instead of BrowserRouter.
import {
Router,
Route,
Switch,
Redirect,
} from "react-router-dom";
Because, BrowserRouter comes with its own history and you provide another one, this causes the problem.
Recently I faced this issue with React 18.
Version of react was 18.0.1 and react-router-dom was 5.1.0 so StrictMode is causing the issue
Just remove or comment the Tag <React.StrictMode> and it should work fine.
Wrap inside Router as given below.
import {
BrowserRouter as Router,
} from "react-router-dom";
<Router><Link to="/forgotPassword">Forgot password?</Link> <Router/>
Related
While navigating back in the react hooks web app, it is not displaying the Home page.Do I need to include anything else, could someone please advise here ? I couldn't see any errors in console.
CSB link added:
https://codesandbox.io/s/blue-sun-rhog82?file=/src/components/home.js
import Select from "react-select";
import { useNavigate } from "react-router-dom";
const options = [
{ value: "test1#test.com", label: "test1#test.com" },
{ value: "test2#test.com", label: "test2#test.com" },
{ value: "test3#test.com", label: "test3#test.com" }
];
const Register = () => {
const navigate = useNavigate();
const moveBack = () => {
navigate("home");
};
return (
<div className="wrapper">
<h1>Lets us Know</h1>
<form>
<input type="text" name="candiate" /> <br></br>
<br></br>
<input type="text" name="mobile" /> <br></br>
<br></br>
<Select isMulti options={options} />
<br></br>
<textarea></textarea>
<hr></hr>
<button>Submit</button>
<button onClick={moveBack}>Back</button>
</form>
</div>
);
};
export default Register;
App.js
import "./styles.css";
import { BrowserRouter, Route, Routes, Switch } from "react-router-dom";
import Home from "./components/home";
import Register from "./components/register";
export default function App() {
return (
<div className="App">
<BrowserRouter>
{/* <Navigation /> */}
<Routes>
<Route path="/" element={<Home />}></Route>
</Routes>
<Routes>
<Route path="/Register" element={<Register />}></Route>
</Routes>
</BrowserRouter>
</div>
);
}
Your issue is:
const moveBack = () => {
navigate("home");
};
Change it to:
const moveBack = () => {
navigate("/");
};
You've specified your </Home> component's rendering route as the root of the directory:
<Route path="/" element={<Home />}></Route>
So nothing was going to render when you navigated to /Home.
Hope this helps.
I am testing a React/Typescript component using Jest and React Testing-Library. I am doing a simple render test and getting a Reference Error: Worker is not defined.
Why and how would I use a worker in this testing context?
Here is my simple test:
import {render, screen} from '#testing-library/react'
import userEvent from '#testing-library/user-event'
import React from 'react'
import {Router} from 'react-router-dom'
import AppRouter from '../router'
test('AppRouter renders all routes and I can navigate to those pages', () => {
render(<AppRouter />)
screen.debug()
})
And here is the AppRouter component:
import { BrowserRouter, Switch, Route, Redirect } from 'react-router-dom'
import { useState } from 'react'
import useLocalStorage from './hooks/useLocalStorage'
import * as Constants from './constants'
import Header from './layout/header/header'
import MainPage from './pages/mainPage/mainPage'
import PostPage from './pages/postPage/postPage'
import UserPage from './pages/userPage/userPage'
import LoginPage from './pages/loginPage/loginPage'
import SignupPage from './pages/signupPage/signupPage'
import NewPage from './pages/newPage/newPage'
import FeedbackPage from './pages/feedbackPage/feedbackPage'
import AdminPage from './pages/adminPage/adminPage'
import SettingPage from './pages/settingPage/settingPage'
import { WebContext } from './context/WebContext'
import Favicon from 'react-favicon'
const AppRouter = () => {
const [adminCode, setAdminCode] = useLocalStorage('admin', '')
const [isMenuOpen, setIsMenuOpen] = useState(false)
const [page, setPage] = useState(Constants.Page.Home)
return (
<BrowserRouter>
<div>
<Favicon url={require('../public/favicon.ico')} />
<WebContext.Provider
value={{
isMenuOpen,
setIsMenuOpen,
page,
setPage,
adminCode,
setAdminCode,
}}
>
<Header />
<Switch>
<Route component={MainPage} path="/" exact={true} />
<Route component={PostPage} path="/post/:id" />
<Route component={UserPage} path="/user" />
<Route component={LoginPage} path="/login" />
<Route component={SignupPage} path="/signup" />
<Route component={NewPage} path="/new" />
<Route component={FeedbackPage} path="/feedback" />
<Route component={AdminPage} path="/admin" />
<Route component={SettingPage} path="/setting" />
<Route component={() => <Redirect to="/" />} />
</Switch>
</WebContext.Provider>
</div>
</BrowserRouter>
)
}
export default AppRouter
Most of what I researched on this were old Jest stackoverflows. I am aware there is a jest-worker package but not sure why I would need this or how I would use it when running this simple test.
Here is a link to jest-worker.
Code Trace:
Header component
import { useContext, useState } from 'react'
import { NavLink, useHistory, useLocation } from 'react-router-dom'
import { observer } from 'mobx-react-lite'
import { WebContext } from '../../context/WebContext'
import UnirepContext from '../../context/Unirep'
import UserContext from '../../context/User'
const Header = () => {
const history = useHistory()
const location = useLocation()
const { isMenuOpen, setIsMenuOpen } = useContext(WebContext)
const [searchInput, setSearchInput] = useState<string>('')
const unirepConfig = useContext(UnirepContext)
const userContext = useContext(UserContext)
const gotoNewPage = () => {
if (
userContext.userState &&
userContext.netReputation >= unirepConfig.postReputation
) {
history.push(`/new`, { isConfirmed: true })
}
}
const gotoUserPage = () => {
history.push(`/user`, { isConfirmed: true })
}
const openMenu = () => {
if (!isMenuOpen) {
console.log('open menu!')
setIsMenuOpen(true)
}
}
const handleSearchInput = (event: any) => {
console.log('search input : ' + event.target.value)
}
return (
<header>
<div className="navLinks">
<NavLink to="/" className="link" activeClassName="active" exact>
<img
src={require('../../../public/images/unirep-title.svg')}
/>
</NavLink>
</div>
{/* <div className="search-bar">
<div className="search-icon"><FaSearch /></div>
<form>
<input type="text" name="searchInput" placeholder="Search by keyword, user names or epoch key" onChange={handleSearchInput} />
</form>
</div> */}
{userContext.userState ? (
<div className="navButtons">
<div id="rep" onClick={gotoUserPage}>
<img
src={require('../../../public/images/lighting.svg')}
/>
{userContext.netReputation}
</div>
<div
id="new"
className={
location.pathname === '/new'
? 'navBtn chosen'
: 'navBtn'
}
>
<img
src={require('../../../public/images/newpost.svg')}
onClick={gotoNewPage}
/>
</div>
<div
id="user"
className={
location.pathname === '/user'
? 'navBtn chosen'
: 'navBtn'
}
>
<img
src={require('../../../public/images/user.svg')}
onClick={gotoUserPage}
/>
</div>
<div className="navBtn">
<img
src={require('../../../public/images/menu.svg')}
onClick={openMenu}
/>
</div>
</div>
) : (
<div className="navButtons">
<div
id="login"
className="whiteButton"
onClick={() => history.push('/login')}
>
Sign in
</div>
<div
id="join"
className="blackButton"
onClick={() => history.push('/signup')}
>
Join
</div>
<div id="menu" className="navBtn">
<img
src={require('../../../public/images/menu.svg')}
onClick={openMenu}
/>
</div>
</div>
)}
</header>
)
}
export default observer(Header)
EDIT
The problem may stem from using MobX for state management and not wrapping the component in a Provider but still unsure of how to do this.
I need help!!!
I have multiple content that is to be rendered after login. I have successfully created the login page and it is working fine.
but i can render the components just by typing the path name in the url bar of my browser so my login page doesn't make any sense.
i have {dashboard, History , employee }
i can just render these components by typing /dashboard, /history without logging in
which should not work like that
THis is my Login.jsx
import React, { useEffect, useState } from "react";
import "./Login.css";
import logo from "../../../Icons/logo.png";
import { useNavigate, useParams, NavLink } from "react-router-dom";
import axios from "axios";
function Login({ setusers }) {
const history = useNavigate();
const [user, setUser] = useState({
userName: "",
password: "",
});
const handlechange = (e) => {
const { name, value } = e.target;
setUser({
...user,
[name]: value,
});
};
const login = () => {
axios.post("/login", user).then((res) => {
alert(res.data.message);
setusers(res.data.user);
history.push("/dashboard")
});
};
return (
<>
<div className="tskms_login-container">
<div className="tskms_login-container-left">
<img src={logo} alt="logo" />
</div>
<div className="tskms_login-container-right">
<p>Login</p>
<form>
<input
type="text"
name="userName"
value={user.userName}
onChange={handlechange}
placeholder="Username"
/>
<input
type="password"
name="password"
value={user.password}
onChange={handlechange}
placeholder="Password"
/>
<h5>Forgot Password?</h5>
{/* <NavLink to={"/dashboard"}> */}
</form>
<button className="tskms_login-right-btn" onClick={login}>
Login
</button>
<div></div>
{/* </NavLink> */}
</div>
</div>
</>
);
}
export default Login;
this is my app.js
import "./App.css";
import { Routes, Route, useNavigate, Navigate } from "react-router-dom";
import Dashboard from "./Components/After Login/Dashboard/Dashboard";
import Task from "./Components/After Login/Task/Task";
import Employee from "./Components/After Login/Employee/Employee";
import History from "./Components/After Login/History/History";
import Notification from "./Components/After Login/Notification/Notification";
import Popup from "./Components/After Login/Task/Popup";
import Addemploye from "./Components/After Login/Employee/Addemploye";
import EmployeeCard from "./Components/After Login/Employee/EmployeeCard";
import Login from "./Components/Before Login/Login/Login";
import { useState } from "react";
function App() {
const [user, setusers] = useState({});
return (
<>
<Routes>
<Route
path="/"
element={
user && user._id ? <Navigate to="/dashboard" /> : <Login setusers={setusers} />
}
/>
<Route path="/login" element={<Login setusers={setusers} />} />
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/task" element={<Task />} />
<Route path="/createtask" element={<Popup />} />
<Route path="/employee" element={<Employee />} />
<Route path="/history" element={<History />} />
<Route path="/notification" element={<Notification />} />
<Route path="/employee" element={<Addemploye />} />
<Route exact path="/employee/:id" element={<EmployeeCard />} />
</Routes>
</>
);
}
export default App;
You can add HOC component, which will detect if there is authz token. If there is not, you can display information like: 'You need to be logged in to enter this page'.
For example:
export default withDashboardHocs(Dashboard)
And HOC component would look like:
export default function withDashboardHocs<C>(
WrappedComponent: React.ComponentType<C>,
redirect = '/login'
) {
return (props: C) => {
const router = useRouter();
const [authzResult] = "API WITH AUTHZ TOKEN"();
const { data, fetching, error } = authzResult;
if (error) {
router.push(redirect);
}
if (fetching) {
return null;
}
if (!data) {
return null;
}
const whoAmI = data.AuthWhoAmI;
return (
<UserContext.Provider value={whoAmI}>
<WrappedComponent {...props} />
</UserContext.Provider>
);
};
}
I am working on an app using useNavigate to go from page to page but am having trouble. Currently, the button click will cause the url to update, but the corresponding page doesn't render. Any help as to what I'm doing wrong?
Here is my button component:
import Fab from '#material-ui/core/Fab';
import AddIcon from '#mui/icons-material/Add';
import { useNavigate } from "react-router-dom";
const FabButton = () => {
let navigate = useNavigate();
return (
<div >
<Fab
onClick={() => navigate(`/add`)}>
<AddIcon />
</Fab>
</div>
)
};
export default FabButton;
My App.js:
import React from 'react';
import { Router, Routes, Route } from 'react-router-dom';
import history from '../history';
import AddPage from './AddPage';
import HomePage from './HomePage';
class App extends React.Component {
render() {
return (
<Router location={history.location} navigator={history} >
<Routes >
<Route exact path="/add" element={ <AddPage title="One Rep - Add" /> } ></Route>
<Route exact path="/" element={ <HomePage title="One Rep - Home" /> } ></Route>
</Routes>
</Router>
)
}
};
export default App;
My history.js:
import { createHashHistory } from 'history';
export default createHashHistory();
My AddPage.js:
export default AddPage;
import React, { useState } from 'react';
import Header from '../components/Header';
import { makeStyles } from '#material-ui/core/styles';
import { TextField, Button, InputAdornment } from '#material-ui/core';
const AddPage = () => {
const [moveData, setMoveData] = useState({ movementName:'', movementWeight: '' });
const handleSubmit = (e) => {
e.preventDefault();
};
return (
<div>
<Header title={"Add Movement"} />
<div>
<form onSubmit={handleSubmit} >
<div>
<TextField
name="movementName"
variant="outlined"
label="Movement Name"
style={{ width:200 }}
value={moveData.movementName}
onChange={(e) => setMoveData({ ...moveData, movementName: e.target.value })}
/>
<TextField
name="movementWeight"
variant="outlined"
label="One Rep Max"
style={{ width:200 }}
value={moveData.movementWeight}
InputProps={{endAdornment: <InputAdornment position="end">lb</InputAdornment>}}
onChange={(e) => setMoveData({ ...moveData, movementWeight: e.target.value })}
/>
</div>
<div>
<Button
variant="contained"
type="submit"
fullWidth >
Submit
</Button>
</div>
</form>
</div>
</div>
);
};
export default AddPage;
I am using react-router-dom ^6.3.0
In react router v6+ you should use BrowserRouter instead of Router. You can see an example in their documentation
So, in your case it should look like this
<BrowserRouter>
<Routes>
<Route
exact
path="/add"
element={<AddPage title="One Rep - Add" />}
></Route>
<Route
exact
path="/"
element={<HomePage title="One Rep - Home" />}
></Route>
</Routes>
</BrowserRouter>
Here is minimal reproduction
PrivateRoute.js
import React from 'react';
import { Route, Navigate } from 'react-router';
const PrivateRoute = ({component:Component, ...rest}) => {
return <Route {...rest} component={(props) =>{
const token = window.localStorage.getItem('token');
if(token){
return <Component {...props} />
}else{
return <Navigate to ={'/signin'}/>
}
}} />
}
export default PrivateRoute;
App.js
import React from "react";
import { createRoot } from "react-dom/client";
import App from "./main";
import { BrowserRouter as Router } from "react-router-dom";
createRoot(document.getElementById("app")).render(
<Router>
<App />
</Router>,
);
export default App;
Main.jsx
import React from "react";
import { Routes, Route } from "react-router-dom";
import Test from "./Test";
function App() {
return (
<div>
<Routes>
<Route path='/' element={<Test />} />
</Routes>
</div>
);
}
ERROR
Error: A <Route> is only ever to be used as the child of <Routes> element, never rendered directly. Please wrap your <Route> in a <Routes>.
Error: A is only ever to be used as the child of element, never rendered directly. Please wrap your in a .
Error: A is only ever to be used as the child of element, never rendered directly. Please wrap your in a .
Error: A is only ever to be used as the child of element, never rendered directly. Please wrap your in a .
I got some error like this. But I use new update of react router v6. Please help me to solve this problem.
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Shopping-Cart</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="app.jsx"></script>
</body>
</html>
app.js
import React from "react";
import { render } from "react-dom";
import { Routes, Route, BrowserRouter as Router } from "react-router-dom";
import InsertUser from "./Containers/User/InsertUser";
import ViewUser from "./Containers/User/ViewUser";
import ViewUsers from "./Containers/User/ViewUsers";
render(
<Router>
<Routes>
<Route path="/" element={<InsertUser />} />
<Route path="/viewUsers" element={<ViewUsers />} />
<Route path="/viewUser/:id" element={<ViewUser />} />
</Routes>
</Router>,
document.getElementById("app")
);
insertUser.jsx
import React, { useState } from "react";
import axios from "axios";
import { Link } from "react-router-dom";
function InsertUser() {
const [firstName, setFirstName] = useState("");
const [lastName, setLastName] = useState("");
const insertData = () => {
const user = {
firstName: firstName,
lastName: lastName,
};
axios
.post("http://localhost:3000/api/user/add", user)
//.then((event) => (window.location.href = "/view-inventory"))
.catch((error) => {
console.error("There was an error!", error);
});
};
return (
<div>
<label for="fname">First name:</label>
<input
type="text"
onChange={(e) => {
setFirstName(e.target.value);
}}
id="fname"
name="fname"
/>
<br />
<label for="lname">Last name:</label>
<input
type="text"
onChange={(e) => {
setLastName(e.target.value);
}}
id="lname"
name="lname"
/>
<br />
<button
onClick={() => {
insertData();
}}
value="Submit"
>
insert
</button>
<br></br>
<button
onClick={() => {
window.location.href = "/viewUsers";
}}
>
View All Users
</button>
</div>
);
}
export default InsertUser;
viweUsers.jsx
import React, { useEffect, useState } from "react";
import axios from "axios";
function ViewUsers() {
const [tableData, setTableData] = useState("");
useEffect(() => {
axios
.get("http://localhost:3000/api/user/getUsers")
.then((response) => {
setTableData(response.data);
})
.catch((error) => {
console.error("There was an error!", error);
});
}, []);
const deteleData = (id) => {
axios
.delete("http://localhost:3000/api/user/delete/" + id)
.then(alert("Deleted"));
};
return (
<div>
<table>
<thead>
<tr>
<th>FirstName</th>
<th>LastName</th>
</tr>
</thead>
<tbody>
{tableData.length > 0
? tableData.map((data) => (
<tr
key={data._id}
onClick={() => {
window.location.href = `/viewUser/${data._id}`;
}}
>
<td>{data.firstName}</td>
<td>{data.lastName}</td>
<td>
<button
onClick={() => {
deteleData(data._id);
}}
>
Delete
</button>
</td>
</tr>
))
: null}
</tbody>
</table>
<br></br>
</div>
);
}
export default ViewUsers;
Route components are only valid as children of a Routes or other Route components, and as the error message says, you can't render it directly in an other component alone. In react-router-dom v6 custom Route components are no more, instead the suggested pattern is to use wrapper components to hold the auth logic and conditionally return the content or a Navigate redirect.
Convert PrivateRoute into a PrivateWrapper:
import { Navigate, Outlet } from "react-router-dom";
const PrivateWrapper = () => {
const token = window.localStorage.getItem('token');
return token ? <Outlet /> : <Navigate to='/signin' replace/>;
}
Render the wrapper around Route components you want to protect access to. The Outlet is where the children routes will be rendered
function App() {
return (
<div className="App">
<Router>
<Routes>
<Route path="/" element={<PrivateWrapper/>}>
<Route path="/" element={ <Home />}/>
</Route>
<Route path="/signin" element={ <Signin />} />
<Route path="/signup" element={ <Signup />} />
</Routes>
</Router>
</div>
);
}