【问题标题】:React Router doesn't load the new page component after redirecting via navigateReact Router 在通过导航重定向后不会加载新的页面组件
【发布时间】:2020-09-29 16:55:34
【问题描述】:

我是 React Hooks 的新手并尝试使用它们 + 实验性 React Router 版本 6 库,因此我不必在将来的某个时候将我的代码从版本 5 升级到 6

注意:版本 6 与版本 5 不兼容,因此没有Switch 不是答案;)

想法:创建一个简单的管理应用程序,您可以通过单击按钮登录/注销。管理页面受到访问锁的保护,因此任何人都无法通过直接 URL 访问它。

问题:单击登录按钮后,URL 更改为/admin,但我看不到管理页面(仅在页面刷新或通过@987654324 强制刷新页面后@)。

这是 React Router v6 中的问题还是我使用 API 的方式错误?源码(https://codesandbox.io/s/quirky-black-rsdy0):

import React, {useEffect, useState} from "react";
import {BrowserRouter, Route, Routes, useNavigate} from "react-router-dom";

async function isLoggedIn() {
    // In real life this function is calling a REST endpoint and awaiting the result
    return localStorage.getItem("loggedin") !== null;
}

async function setLoginStatus(status) {
    // In real life this function is calling a REST endpoint and awaiting the result
    if (status === true) {
        localStorage.setItem("loggedin", true);
    } else {
        localStorage.removeItem("loggedin");
    }
}

function LoginPage() {
    const navigate = useNavigate();

    async function handleLogin(event) {
        event.preventDefault();
        await setLoginStatus(true);
        navigate("/admin"); // --> The user is logged in and the URL changes to /admin, but he doesn't see the admin page (Only after a page refresh)
        //window.location.reload(); // Hack to reload the page to see the admin page
    }

    return (
        <form onSubmit={handleLogin}>
            <button type="submit">Login</button>
        </form>
    );
}

function LogoutPage() {
    const navigate = useNavigate();

    useEffect(() => {
        const logoutAccount = async () => {
            await setLoginStatus(false);
            navigate("/login");
        };
        logoutAccount();
    });

    return <p>Logging out</p>;
}

function AdminPage() {
    return (
        <div>
            <p>The secured admin page</p>
            <a href="/logout">Logout</a>
        </div>
    );
}

function AuthenticationLock(props) {
    if (props.isLoggedIn === true) {
        return <div>{props.children}</div>;
    } else {
        return <LoginPage/>;
    }
}

export default function App() {

    const [loginStatus, setAppLoginStatus] = useState(false);

    useEffect(() => {
        const login = async () => {
            const value = await isLoggedIn();
            setAppLoginStatus(value);
        };
        login();
    });

    return (
        <BrowserRouter>
            <AuthenticationLock isLoggedIn={loginStatus}>
                <Routes>
                    <Route path="/" element={<AdminPage/>}/>
                    <Route path="/admin" element={<AdminPage/>}/>
                    <Route path="/login" element={<LoginPage/>}/>
                    <Route path="/logout" element={<LogoutPage/>}/>
                    <Route path="/*" element={<AdminPage/>}/>
                </Routes>
            </AuthenticationLock>
        </BrowserRouter>
    );
}

【问题讨论】:

    标签: javascript reactjs react-router react-router-dom


    【解决方案1】:

    不知何故,我的AuthenticationLock 对这个问题负责。我换了一个不同的设计,现在它可以工作了:

    import React, { useEffect } from "react";
    import {
      BrowserRouter,
      Route,
      Routes,
      Link,
      useNavigate
    } from "react-router-dom";
    
    function isLoggedIn() {
      // In real life this function is calling a REST endpoint and awaiting the result
      return localStorage.getItem("loggedin") !== null;
    }
    
    async function setLoginStatus(status) {
      // In real life this function is calling a REST endpoint and awaiting the result
      if (status) {
        localStorage.setItem("loggedin", "1");
      } else {
        localStorage.removeItem("loggedin");
      }
    }
    
    function DashboardPage() {
      return (
        <div>
          <h1>Dashboard page</h1>
          <p>This is the dashboard page</p>
          <Link to={"/accounts"}>Accounts</Link>
          <br />
          <Link to={"/logout"}>Logout</Link>
        </div>
      );
    }
    
    function AccountsPage() {
      return (
        <div>
          <h1>Accounts page</h1>
          <p>This is the accounts page</p>
          <Link to={"/dashboard"}>Dashboard</Link>
          <br />
          <Link to={"/logout"}>Logout</Link>
        </div>
      );
    }
    
    function LoginPage() {
      const navigate = useNavigate();
    
      async function handleLogin(event) {
        event.preventDefault();
        await setLoginStatus(true);
        navigate("/dashboard");
      }
    
      return (
        <div>
          <h1>Login</h1>
          <form onSubmit={handleLogin}>
            <button type="submit">Login</button>
          </form>
        </div>
      );
    }
    
    function LogoutPage() {
      const navigate = useNavigate();
    
      useEffect(() => {
        const logoutAccount = async () => {
          await setLoginStatus(false);
          navigate("/login");
        };
        logoutAccount();
      });
    
      return <p>Logging out</p>;
    }
    
    const PrivateRoute = ({ component, ...rest }) => {
      if (isLoggedIn()) {
        const routeComponent = props => React.createElement(component, props);
        return <Route {...rest} render={routeComponent} />;
      } else {
        return <LoginPage />;
      }
    };
    
    function App() {
      return (
        <BrowserRouter>
          <Routes>
            <PrivateRoute path="/" element={<DashboardPage />} />
            <PrivateRoute path="/dashboard" element={<DashboardPage />} />
            <PrivateRoute path="/accounts" element={<AccountsPage />} />
            <Route path="/login" element={<LoginPage />} />
            <PrivateRoute path="/logout" element={<LogoutPage />} />
            <PrivateRoute path="/*" element={<DashboardPage />} />
          </Routes>
        </BrowserRouter>
      );
    }
    
    export default App;
    

    【讨论】:

    • 虽然渲染方法不再是路由器 v6 中 Route 属性的一部分。
    【解决方案2】:

    所以在这种情况下,我们需要使用&lt;Switch&gt; &lt;/Switch&gt; 组件,它需要包装所有&lt;Route /&gt; 组件,以便一次渲染一条路由。

    始终为所有组件设置exact bool,这样就不会将route='/'route='/about' 误认为两者都有/ 字符或者如果我们有这样的路由:/about/about/blabla。所以交换机会知道要渲染什么&lt;Route /&gt;

    如果您想为不存在的链接添加&lt;NotFoundPage /&gt; 组件。您只需将 component prop 分配给 &lt;Route /&gt; 组件,例如:

    <Switch>
      <Route exact path='/' />
      <Route exact path='/about' />
      <Route component={NotFoundPage} />
    </Switch>
    

    【讨论】:

    • 如另一条评论中所述:React Router 版本 6 不再提供 Switch
    【解决方案3】:

    围绕&lt;Switch&gt;&lt;/Switch&gt; 包装所有路线,并在除最后一条之外的所有路线上设置exact={true} 属性。

    import {BrowserRouter, Route, Routes, Switch, useNavigate} from "react-router-dom";
    
    [...]
    
        return (
            <BrowserRouter>
                <AuthenticationLock isLoggedIn={loginStatus}>
                    <Routes>
                        <Switch>
                            <Route path="/" exact={true} element={<AdminPage/>}/>
                            <Route path="/admin" exact={true} element={<AdminPage/>}/>
                            <Route path="/login" exact={true} element={<LoginPage/>}/>
                            <Route path="/logout" exact={true} element={<LogoutPage/>}/>
                            <Route path="/*" element={<AdminPage/>}/>
                        </Switch>
                    </Routes>
                </AuthenticationLock>
            </BrowserRouter>
        );
    

    【讨论】:

    • 我想你在 React Router 版本 5 中仍然指的是Switch?路由器版本 6 alpha 5 不再有 Switch,而是单个组合 Routes
    猜你喜欢
    • 2017-04-15
    • 2022-12-11
    • 2020-07-04
    • 1970-01-01
    • 2021-06-29
    • 1970-01-01
    • 2022-06-28
    • 2022-01-22
    • 2016-08-09
    相关资源
    最近更新 更多