简体   繁体   中英

React router dom redirect problem. Changes url, does not render component

Problem: When I use history.push() , I can see that browser changes url, but it does not render my component listening on the path. It only renders if I refresh a page.

App.js file:

import React from "react";
import { BrowserRouter as Router, Route } from "react-router-dom";
import { Provider } from "react-redux";
import PropTypes from "prop-types";

//Components
import LoginForm from "../LoginForm/LoginForm";
import PrivateRoute from "../PrivateRoute/PrivateRoute";
import ServerList from "../ServerList/ServerList";

const App = ({ store }) => {
  const isLoggedIn = localStorage.getItem("userToken");

  return (
    <Router>
      <Provider store={store}>
        <div className="App">
          {isLoggedIn !== true && (
            <Route exact path="/login" component={LoginForm} />
          )}
          <PrivateRoute
            isLoggedIn={!!isLoggedIn}
            path="/"
            component={ServerList}
          />
        </div>
      </Provider>
    </Router>
  );
};

App.propTypes = {
  store: PropTypes.object.isRequired
};

export default App;

Inside my LoginForm , I am making a request to an API, and after doing my procedures, I use .then() to redirect my user:

.then(() => {
  props.history.push("/");
})

What happens: Browser changes url from /login to / , but component listening on / route is not rendered, unless I reload page.

Inside my / component, I use useEffect() hook to make another request to API, which fetches data and prints it inside return() . If I console.log inside useEffect() it happens twice, I assume initial one, and when I store data from an API inside component's state using useState() hook.

EDIT: adding PrivateRoute component as requested:

import React from "react";
import { Route, Redirect } from "react-router-dom";

const PrivateRoute = ({ component: Component, isLoggedIn, ...rest }) => {
  return (
    <Route
      {...rest}
      render={props =>
        isLoggedIn === true ? (
          <Component {...props} />
        ) : (
          <Redirect to={{ pathname: "/login" }} />
        )
      }
    />
  );
};

export default PrivateRoute;

What I tried already: 1) Wrapping my default export with withRouter() :

export default withRouter(connect(mapStateToProps, mapDispatchToProps)(LoginForm));

2) Creating custom history and passing it as prop to Router .

react-router-dom version is ^5.0.1 . react-router is the same, 5.0.1

You have at two mistakes in your code.

  1. You are not using <switch> component to wrap routes. So all routes are processed at every render and all components from each <route> are rendered.

  2. You are using local store to exchange information between components. But change in local store is invisible to react, so it does not fire component re-rendering. To correct this you should use local state in App component (by converting it to class or using hooks).

So corrected code will look like

const App = ({ store }) => {
  const [userToken, setUserToken] = useState(localStorage.getItem("userToken")); // You can read user token from local store. So on after token is received, user is not asked for login

  return (
    <Router>
      <Provider store={store}>
        <div className="App">
          <Switch>
            {!!userToken !== true && (
              <Route exact path="/login"
                render={props => <LoginForm {...props} setUserToken={setUserToken} />}
              />
            )}
            <PrivateRoute
              isLoggedIn={!!userToken}
              path="/"
              component={ServerList}
            />
          </Switch>
        </div>
      </Provider>
    </Router>
  );
};

And LoginForm should use setUserToken to change user token in App component. It also may store user token in local store so on page refresh user is not asked for login, but stored token is used.

Also be sure not to put anything between <Switch> and </Switch> except <Route> . Otherwise routing will not work.

Here is working sample

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM