I try to redirect the user after login in my React App, but Navigate won't work and i don't know why...
Here is my code and thanks for your help
import React, { Component } from "react";
import {Route, Navigate} from 'react-router-dom';
import Bouton from "../components/Bouton";
class Dallan extends Component{
logout = () =>{
localStorage.removeItem('logged');
return <Navigate to= '/login' />;
}
render(){
return(
<Bouton typeBtn = 'btn-danger' click={() => this.logout()}>Deconnexion</Bouton>
)
}
}
export default Dallan;
And in my App.js
function App() {
let session = localStorage.getItem('logged');
return (
<BrowserRouter>
<Routes>
<Route path="/" element={session ? <Navigate to="/dallan" /> : <Login/>} />
<Route path='/dallan' element={<Dallan/>}/>
</Routes>
</BrowserRouter>
);
}
export default App;
If you are using react route dom v6 which I assume you are by the use of <Navigate /> then <Navigate /> is a component which would need to be rendered to work. You are just returning it to nothing so it obviously won't render. You want to use the useNavigate() hook instead. But you will want to use a function component to use the hook. Like so:
import React, { Component } from "react";
import {Route, useNavigate} from 'react-router-dom';
import Bouton from "../components/Bouton";
function Dallan() {
const navigate = useNavigate();
const logout = () =>{
localStorage.removeItem('logged');
navigate('/login')
}
return(
<Bouton typeBtn = 'btn-danger' click={() => logout()}>Deconnexion</Bouton>
)
}
export default Dallan;
technically it should work try writing it like so, return <Navigate replace={true} to='/' /> enssure that you place the replace={true} because its job is to redirect meaning it replaces everything on the page with where you are redirecting to user to
Related
I am making a mern application with login/registration system and the dashboard and the other routes that will only be accessible when someone logs in now the problem is i was trying to write a if condition in react router dom so that all the routes inside the dashboard could be resitricted till the user logs in , and i use 'useNavigate' hook to go back to login page if user is not logged in but the application gives me error saying useNavigate() may be used only in the context of a component , but i used the same hook in my other component too where i didnt used any router and it worked fine there and so i am not able to understand what to do , also i want to know how can i put the component name inside a variable so that if i call the function i can put the component name in it and later put that varibale like this <component_name/> and it should change its value , here is my code:-
import Navbar from "./components/navbar/Navbar";
import Home from "./components/Home/Home";
import 'bootstrap/dist/css/bootstrap.min.css'
import Forgot_password from "./components/login_and_reg/Forgot_password/Forgot_password";
import Weather from "./components/Weather/Weather";
import Landing_page from './components/login_and_reg/Landing_page/Landing_page'
import {
BrowserRouter as Router,
Route,
Routes,
useNavigate
} from "react-router-dom";
import Verification from "./components/login_and_reg/Verification/Verification";
import Protected_routes from './components/Protected_routes'
import { useSelector } from "react-redux";
function App() {
const loggedin = useSelector(state => state.loggedin)
const Navigate = useNavigate();
const rememberMe = localStorage.getItem('rememberMe')
function checkroute(Component_name){
if (rememberMe=='true') {
return <Component_name/>
} else {
console.log(loggedin)
if(loggedin =='loggedin'){
return <Component_name/>
}
else{
Navigate('/')
}
}
}
return (
<>
<Router>
{/* <Navbar/> */}
<Routes>
<Route path="/weather" element={checkroute(Weather)}></Route>
<Route exact path="/" element={<Protected_routes/>}></Route>
<Route path="/verify/:first_name/:email" element={<Verification/>}></Route>
<Route path="/forgot_password" element={<Forgot_password/>}></Route>
{/* <Route exact path="/home" element={<Protected_routes/>}></Route> */}
</Routes>
</Router>
</>
);
}
I also made a protected route only for login purpose but i dont know how to use it for all the components if it is possible then here is code of that component:-
import React, { Component } from 'react';
import { useSelector } from 'react-redux';
import {Navigate, Route , useNavigate} from 'react-router-dom';
import Home from './Home/Home';
import Landing_page from './login_and_reg/Landing_page/Landing_page';
const Protected_routes = () => {
const loggedin = useSelector(state => state.loggedin)
const Navigate = useNavigate();
const rememberMe = localStorage.getItem('rememberMe')
if (rememberMe=='true') {
return <Home/>
} else {
if(loggedin=='loggedin'){
return <Home/>
}
else{
return <Landing_page/>
}
}
}
export default Protected_routes
export default App;
What you'd likely use a PrivateRoute component to wrap your secured pages. It will render the desired page if not logged in, or redirect to the login page.
Here's the flow:
1. Define a private route component:
// PrivateRoute.ts
import { Navigate } from 'react-router-dom';
import { useSelector } from 'react-redux';
import React from 'react';
export function PrivateRoute({ children }: { children: React.ReactElement }) {
// the user verification logic is up to your application
// this is an example based on your code above
const loggedin = useSelector(state => state.loggedin);
const rememberMe = localStorage.getItem('rememberMe');
if (rememberMe==='true') {
return <Navigate to={'/home'} />;
}
else if (loggedin==='loggedin'){
// render the wrapped page
return children;
}
else {
// user not logged in, redirect to the Login page which is unprotected
return <Navigate to={'/login'} />;
}
}
2. And use it in your router :
I assume you want to protect your Home and Wheater pages here. You can customize it to your own logic.
import React from 'react';
import { Navigate, Route, Routes } from 'react-router-dom';
import { PrivateRoute } from './PrivateRoute';
import { Home } from "./my-home-page";
import { Wheater } from "my-wheater-page";
import { Login } from "my-login-page";
export const AppRoutes = () => {
return (
<Routes>
<Route
path='home'
element={
<PrivateRoute>
<Home/>
</PrivateRoute>
}
/>
<Route
path='login'
element={
<Login/>
}
/>
<Route
path='wheater'
element={
<PrivateRoute>
<Wheater />
</PrivateRoute>
}
/>
</Routes>
);
};
I am trying to understand why Header component doesn't get updated when I click Button.
I believe that the problem is that I am not calling with Router. But why then App.js doesn't re render when I switch routes?
import React, { useEffect } from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import First from './First';
import Second from './Second';
import Third from './Third';
import Header from './Header';
function App() {
return (
<div>
<Header />
<Router>
<Switch>
<Route exact path={'/'} component={First} />
<Route exact path={'/first'} component={Second} />
<Route exact path={'/second'} component={Third} />
</Switch>
</Router>
</div>
);
}
export default App;
import React from 'react';
export default function First(props) {
console.log('🚀 ~ file: First.js ~ line 4 ~ First ~ props', props);
return (
<div>
First
<button
onClick={() => {
props.history.push({
pathname: '/second',
});
}}
>
Go to Second
</button>
</div>
);
}
so my condition here doesn't get fired when path changes. the reason is that component hasn't been called and old condition is still there
import React from 'react'
export default function Header() {
console.log(window.location.pathname);
const logger = window.location.pathname === '/third' ? (<div>This is second</div>) :
(<div>this is root</div>)
return logger
}
I know that I can call Header somewhere else, but what is problem in this showcase?
The Header component is being rendered outside the Router, so it's not rerendered or made aware of route changes.
I suggest moving the Header component into the Router and have it access the route props.
App
function App() {
return (
<div>
<Router>
<Header /> // <-- move into Router
<Switch>
<Route exact path={'/'} component={First} />
<Route exact path={'/first'} component={Second} />
<Route exact path={'/second'} component={Third} />
</Switch>
</Router>
</div>
);
}
Header
import { useLocation } from 'react-router-dom';
export default function Header() {
const location = useLocation();
console.log(location.pathname);
return location.pathname === '/third'
? <div>This is second</div>
: <div>this is root</div>;
}
Alternatively you could use the useRouteMatch hook:
import { useRouteMatch } from 'react-router-dom';
export default function Header() {
const match = useRouteMatch('/third');
return match
? <div>This is second</div>
: <div>this is root</div>;
}
I have a redirect code inside an if else of a component, in which I needed to validate first the user before the app decides where to go next. In this scenario, the user has entered in he needs to be redirected to a component called "EventEnd" with msg of "The event has ended, come back again soon".
Pls note that the code <Redirect/> changes the url in the browser, just that the page is empty/blank, and during debug it is not hitting inside my EventEnd component, so It's totally not going there. Also, if I enter the url manually in the browser like this works, it renders the page as I wanted.
http://localhost:3000/0c546bbcc435cd3cb2751e4e5e36956fd9248136/event-end
here are the codes I tried for redirecting - all didn't work.
import React, { useEffect, useState } from "react";
const RouteUserToEventPage: React.FC<{}> = (props: any) => {
.........
const location = useLocation();
useEffect(() => {
if (eventId)
{
getUserStatus(); //this is where I set the eventStatus
}
}, [location])
if (eventStatus === status.Late) {
return (
<Redirect exact
to={{ pathname: `/${eventId}/event-end`}}
/>
)}
else {
return <>test page</>
}
};
export default RouteUserToEventPage;
this is my route file called index.tsx
export const PrivateRoutes: React.FC<any> = (props: any) => {
const appModel = props.appModel;
const hasEventInfo:boolean = appModel.event.value && appModel.event.value.event_id? true: false;
if (!hasEventInfo){
return <Event.RouteUserToEventPage {...props} appModel={appModel}></Event.RouteUserToEventPage>
}
return (
<Switch>
<Redirect to="/:eventId/event-end" />
<Route path="/:eventId/event-end" exact component={EventEnd} />
.....
<Route component={PageNotFound} path="/page-not-found" />
<Redirect to="/page-not-found" />
</Switch>
);
};
things I have tried which all didn't work:
I have also tried adding export default withRouter(RouteUserToEventPage)
I have also tried returning a button then I can click with redirect
I have added this to my route file:
<Route
path={[ '/:eventId/event-end']}>
<Switch>
<Redirect to="/:eventId/event-end" /> -- originally wasnt there but i added this at some point coz of trial and error
<Route path="/:eventId/event-end" exact component={EventEnd} />
</Switch>
<Route/>
I added exact, I removed push from the redirect code
I made a very simple btn that calls this (calls another component which loads if I put in Link to={{...}} just to test other component (coz I thought maybe it's my component that has problem), but that also didn't work on redirect
const redirectTest = () => {
let id = "0c546e346cd00dace9dcf18f8389adb537c12c85";
<Redirect push to={{ pathname: `/some-settings/${id}`}}
/>
}
pls. do not suggest history as it doesn't apply in this. Also, I cannot use a btn click and even if I did, the sample redirectTest doesn't work.
What am I missing here? It's been my whole day so far.
UPDATE:
import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import { BrowserRouter as Router, Route, Switch, Link } from "react-router-dom";
import store from "./store/store";
import { Provider } from "react-redux";
import * as agent from "./utils/agent";
import * as actionCreators from "./store/actionCreators";
import { useDispatch } from "react-redux";
import { TableContainer, Paper, Table, TableHead, TableRow, TableCell, TableBody, TableFooter, TablePagination } from "#material-ui/core";
ReactDOM.render(
<Provider store={store}>
<Router>
<Switch>
<Route path="/:organization" component={App} />
<Route
component={() => {
const [organizations, setOrganizations] = useState<organization[]>(
[]
);
const dispatch = useDispatch();
const getAllOrganizations = () => {
......
};
useEffect(getAllOrganizations, [dispatch]);
return (
<>
<TableContainer component={Paper} className={"organizations-list"}>
.............
</TableContainer>
</>
);
}}
/>
</Switch>
</Router>
</Provider>,
document.getElementById("root")
);
reportWebVitals();
Good evening everyone,
I have been trying to add withRouter to my react app so it does not break because of the connect function (see code below).
My code is working, but when i add withRouter to the line below, it breaks my app with the following message :
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(App));
Error: Invariant failed: You should not use <withRouter(Connect(App)) /> outside a Router>
i found this topic : Invariant failed: You should not use <Route> outside a <Router> but it's not helping me with me issue when i try to replace with a single Router
Please find below the whole code used :
App.js
import React, {useEffect}from 'react';
import { BrowserRouter } from 'react-router-dom'
import { Route, Redirect} from 'react-router-dom'
import { withRouter } from 'react-router-dom'
import { connect } from 'react-redux'
import * as actions from './store/actions/index'
// Composants
import Layout from './components/hoc/Layout/Layout'
import BudgetHandler from './components/BudgetHandler/BudgetHandler'
import DashBoard from './components/DashBoard/DashBoard'
import Movements from './components/Movements/Movements'
import Home from './components/Home/Home'
import Logout from './components/Logout/Logout'
import classes from './App.module.css'
const App = (props) => {
useEffect(() => {
props.onTryAutoSignup()
},[])
let routes = <React.Fragment>
<Route path="/" exact component={Home} />
<Redirect to="/" />
</React.Fragment>
if(props.isAuthentificated) {
routes = <React.Fragment>
<Route path="/movements" component={Movements} />
<Route path="/dashboard" component={DashBoard} />
<Route path="/logout" component={Logout} />
<Route path="/handler" component={BudgetHandler} />
<Redirect to="/dashboard" />
</React.Fragment>
}
return (
<div className={classes.App}>
<BrowserRouter>
<Layout>
{routes}
</Layout>
</BrowserRouter>
</div>
);
}
const mapStateToProps = state => {
return {
isAuthentificated: state.auth.token !== null
}
}
const mapDispatchToProps = dispatch => {
return {
onTryAutoSignup: () => dispatch(actions.authCheckState())
}
}
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(App));
And this is happening because i am trying to add this function to the useEffect hook to check permanently if the user is auth or not :
in actions/auth.js
export const authCheckState = () => {
return dispatch => {
const token = localStorage.getItem('token')
if(!token) {
dispatch(logout())
} else {
const expirationTime = new Date(localStorage.getItem('expirationDate'))
const userId = localStorage.getItem('userId')
if(expirationTime > new Date()){
dispatch(logout())
} else {
dispatch(finalSignIn(token, userId))
dispatch(checkAuthTimeout(expirationTime.getSeconds() - new Date().getSeconds()))
}
}
}
}
Thank you for your help
Have a good evening
withRouter can only be used in children components of element. In your case can be used with Movements, DashBoard and other childrens. use it while exporting Movements like
export default withRouter(Movements)
on Movements page.
I am developing an application in which I check if the user is not loggedIn. I have to display the login form, else dispatch an action that would change the route and load other component. Here is my code:
render() {
if (isLoggedIn) {
// dispatch an action to change the route
}
// return login component
<Login />
}
How can I achieve this as I cannot change states inside the render function.
Considering you are using react-router v4
Use your component with withRouter and use history.push from props to change the route. You need to make use of withRouter only when your component is not receiving the Router props, this may happen in cases when your component is a nested child of a component rendered by the Router and you haven't passed the Router props to it or when the component is not linked to the Router at all and is rendered as a separate component from the Routes.
import {withRouter} from 'react-router';
class App extends React.Component {
...
componenDidMount() {
// get isLoggedIn from localStorage or API call
if (isLoggedIn) {
// dispatch an action to change the route
this.props.history.push('/home');
}
}
render() {
// return login component
return <Login />
}
}
export default withRouter(App);
Important Note
If you are using withRouter to prevent updates from being blocked by
shouldComponentUpdate, it is important that withRouter wraps the
component that implements shouldComponentUpdate. For example, when
using Redux:
// This gets around shouldComponentUpdate
withRouter(connect(...)(MyComponent))
// This does not
connect(...)(withRouter(MyComponent))
or you could use Redirect
import {withRouter} from 'react-router';
class App extends React.Component {
...
render() {
if(isLoggedIn) {
return <Redirect to="/home"/>
}
// return login component
return <Login />
}
}
With react-router v2 or react-router v3, you can make use of context to dynamically change the route like
class App extends React.Component {
...
render() {
if (isLoggedIn) {
// dispatch an action to change the route
this.context.router.push('/home');
}
// return login component
return <Login />
}
}
App.contextTypes = {
router: React.PropTypes.object.isRequired
}
export default App;
or use
import { browserHistory } from 'react-router';
browserHistory.push('/some/path');
In react-router version 4:
import React from 'react'
import { BrowserRouter as Router, Route, Redirect} from 'react-router-dom'
const Example = () => (
if (isLoggedIn) {
<OtherComponent />
} else {
<Router>
<Redirect push to="/login" />
<Route path="/login" component={Login}/>
</Router>
}
)
const Login = () => (
<h1>Form Components</h1>
...
)
export default Example;
Another alternative is to handle this using Thunk-style asynchronous actions (which are safe/allowed to have side-effects).
If you use Thunk, you can inject the same history object into both your <Router> component and Thunk actions using thunk.withExtraArgument, like this:
import React from 'react'
import { BrowserRouter as Router, Route, Redirect} from 'react-router-dom'
import { createBrowserHistory } from "history"
import { applyMiddleware, createStore } from "redux"
import thunk from "redux-thunk"
const history = createBrowserHistory()
const middlewares = applyMiddleware(thunk.withExtraArgument({history}))
const store = createStore(appReducer, middlewares)
render(
<Provider store={store}
<Router history={history}>
<Route path="*" component={CatchAll} />
</Router
</Provider>,
appDiv)
Then in your action-creators, you will have a history instance that is safe to use with ReactRouter, so you can just trigger a regular Redux event if you're not logged in:
// meanwhile... in action-creators.js
export const notLoggedIn = () => {
return (dispatch, getState, {history}) => {
history.push(`/login`)
}
}
Another advantage of this is that the url is easier to handle, now, so we can put redirect info on the query string, etc.
You can try still doing this check in your Render methods, but if it causes problems, you might consider doing it in componentDidMount, or elsewhere in the lifecycle (although also I understand the desire to stick with Stateless Functional Compeonents!)
You can still use Redux and mapDispatchToProps to inject the action creator into your comptonent, so your component is still only loosely connected to Redux.
This is my handle loggedIn. react-router v4
PrivateRoute is allow enter path if user is loggedIn and save the token to localStorge
function PrivateRoute({ component: Component, ...rest }) {
return (
<Route
{...rest}
render={props => (localStorage.token) ? <Component {...props} /> : (
<Redirect
to={{
pathname: '/signin',
state: { from: props.location },
}}
/>
)
}
/>
);
}
Define all paths in your app in here
export default (
<main>
<Switch>
<Route exact path="/signin" component={SignIn} />
<Route exact path="/signup" component={SignUp} />
<PrivateRoute path="/" component={Home} />
</Switch>
</main>
);
Those who are facing issues in implementing this on react-router v4. Here is a working solution for navigating through the react app programmatically.
history.js
import createHistory from 'history/createBrowserHistory'
export default createHistory()
App.js OR Route.jsx. Pass history as a prop to your Router.
import { Router, Route } from 'react-router-dom'
import history from './history'
...
<Router history={history}>
<Route path="/test" component={Test}/>
</Router>
You can use push() to navigate.
import history from './history'
...
render() {
if (isLoggedIn) {
history.push('/test') // this should change the url and re-render Test component
}
// return login component
<Login />
}
All thanks to this comment: https://github.com/ReactTraining/react-router/issues/3498#issuecomment-301057248
render(){
return (
<div>
{ this.props.redirect ? <Redirect to="/" /> :'' }
<div>
add here component codes
</div>
</div>
);
}
I would suggest you to use connected-react-router https://github.com/supasate/connected-react-router
which helps to perform navigation even from reducers/actions if you want.
it is well documented and easy to configure
I was able to use history within stateless functional component, using withRouter following way (needed to ignore typescript warning):
import { withRouter } from 'react-router-dom';
...
type Props = { myProp: boolean };
// #ts-ignore
export const MyComponent: FC<Props> = withRouter(({ myProp, history }) => {
...
})
import { useNavigate } from "react-router-dom"; //with v6
export default function Component() {
const navigate = useNavigate();
navigate.push('/path');
}
I had this issue and just solved it with the new useNavigate hook in version 6 of react-router-dom