【问题标题】:Problem when reloading page using react-router-dom使用 react-router-dom 重新加载页面时出现问题
【发布时间】:2021-08-16 08:08:20
【问题描述】:

我设法创建了一条私人路线并使用react-router-dom 导航到不同的页面。但是,当我导航到一个页面并重新加载它时,它首先会转到 /login 半秒钟,然后正确地重新加载页面。如何防止这种不良行为并改进我的路由?

这是我的路线:

    <Router>
      <Route
        path="/"
        component={() =>
          !auth ? <Redirect to="/login" /> : <Redirect to={path} />
        }
      />
      <Route exact path="/home" component={Home} />
      <Route exact path="/dashboard" component={Dashboard} />
      <Route exact path="/login" component={RedirectPage} />
    </Router>

这是完整的组件:

import {
  Route,
  BrowserRouter as Router,
  Link,
  Redirect,
} from "react-router-dom";
import { Container, Button } from "@material-ui/core/";
import Login from "./Login";
import { useContext,useState } from "react";
import { UserContext } from "../App";
import { signOut } from "../Storage/Auth";

const Routes = () => {
  const { auth, setAuth, logging } = useContext(UserContext);
  const [path,setPath] = useState("/home")
  const handleSignOut = () => {
    signOut(setAuth);
    console.log("Auth", auth);
  };

  const Home = () => {
    console.log("Home");
    return (
      <Container>
        <h1>Welcome</h1>
        <Link to="/">
          <Button onClick={handleSignOut}> Log Out</Button>
        </Link>
        <Link to="/dashboard">
          <Button> Dash</Button>
        </Link>
      </Container>
    );
  };

  const Dashboard = () => {
    setPath("/dashboard")
    console.log("Dash");
    return (
      <Container>
        <Link to="/home">
          <Button> HOME</Button>
        </Link>
        <h1>Dashboard</h1>
      </Container>
    );
  };

  const RedirectPage = () => {
    if (!logging) {
      return <div></div>;
    } else {
      return <Login />;
    }
  };

  return (
    <Router>
      <Route
        path="/"
        component={() =>
          !auth ? <Redirect to="/login" /> : <Redirect to={path} />
        }
      />
      <Route exact path="/home" component={Home} />
      <Route exact path="/dashboard" component={Dashboard} />
      <Route exact path="/login" component={RedirectPage} />
    </Router>
  );
};

export { Routes };

这是我的Login 组件。

import { useState, useContext } from "react";
import {
  Button,
  Card,
  Container,
  Typography,
  Box,
  TextField,
} from "@material-ui/core/";
import { useHistory} from "react-router-dom";
import { signIn } from "../Storage/Auth";
import { UserContext } from "../App";

const Login = () => {
  const [mail, setMail] = useState<string>("");
  const [password, setPassword] = useState<string>("");
  const { user, setUser } = useContext(UserContext);

  const handleSignIn = async (m: string, p: string) => {
    await signIn(m, p).then((e) => {
      console.log("USERID", e, user);
      setUser(e);
    });
  };

  const history = useHistory();
  const handleEnter = () => {
    history.push("/home");
  };

  const handleOnKey = (e: any) => {
    if (e.key === "Enter") {
      e.preventDefault();
      handleSignIn(mail, password);
      handleEnter();
    }
  };

  return (
    <Card className="Card" raised={true}>
      <Container className="Input">
        <Typography className="Sign-in" paragraph={true} variant="inherit">
          Sign in
        </Typography>
        <Box
          className="Box"
          borderColor="error.main"
          border={2}
          borderRadius="borderRadius"
        >
          <Container>
            <TextField
              fullWidth={true}
              placeholder=" email"
              value={mail}
              onChange={(e) => {
                setMail(e.target.value);
              }}
              onKeyDown={(e) => {
                handleOnKey(e);
              }}
            />
          </Container>
        </Box>
      </Container>
      <Container className="Input">
        <Box
          className="Box"
          borderColor="error.main"
          borderRadius="borderRadius"
          border={2}
        >
          <Container>
            <TextField
              fullWidth={true}
              placeholder=" password"
              value={password}
              onChange={(e) => {
                setPassword(e.target.value);
              }}
              type="password"
              onKeyDown={(e) => {
                handleOnKey(e);
              }}
            />
          </Container>
        </Box>
        <h1> </h1>
        <Button
          onClick={() => {
            handleSignIn(mail, password);
          }}
          fullWidth={true}
          color="primary"
          variant="contained"
          type="submit"
        >
          Sign In{" "}
        </Button>
        <h1> </h1>
        <Box className="Sign-in">
            <Button size="small"> Register </Button>
        </Box>
        <h1> </h1>
      </Container>
    </Card>
  );
};

export default Login;

这是App 组件:

import { useEffect } from "react";
import { Routes } from "./Routing/Routes";
import "./App.css";
import { Container } from "@material-ui/core/";
import initFirebase from "./Storage/Secret";
import { useState, createContext } from "react";
import { onAuthChange } from "./Storage/Auth";

export const UserContext = createContext<any>(null);
function App() {
  const [user, setUser] = useState(null);
  const [auth, setAuth] = useState<string | null>("");
  const [logging, setLogging] = useState(null)
  useEffect(() => {
    initFirebase();
  }, []);

  useEffect(() => {
    onAuthChange(setAuth,setLogging);
  }, [auth]);

  return (
    <UserContext.Provider value={{ user, setUser, auth,setAuth,logging }}>
      <div className="App">
        <Container>
          <Routes />
        </Container>
      </div>
    </UserContext.Provider>
  );
}

export default App;

另外,这里是auth 逻辑:

import firebase from "firebase/app";
import "firebase/auth";

const auth = () => firebase.auth();

const signIn = async (email, password) => {
  await auth()
    .signInWithEmailAndPassword(email, password)
    .then((userCredential) => {
      var user = userCredential.user;
      console.log("USER", user);
      return user.uid;
    })
    .catch((error) => {
      var errorCode = error.code;
      var errorMessage = error.message;
      alert(errorCode, errorMessage);
      return null;
    });
};

const onAuthChange = (setState, setLoading) => {
  auth().onAuthStateChanged((u) => {
    if (!u) {
      console.log(u);
      setLoading(true);
    } else {
      setState(u);
      setLoading(false);
    }
  });
};

const signOut = (setState) => {
    auth()
    .signOut()
    .then(function () {
      console.log("LOGGED OUT");
    })
    .catch(function (error) {
      console.log("ERROR LOGGING OUT");
    });
  setState(null);
};
export { signIn, signOut, onAuthChange }

最后,完整代码在https://gitlab.com/programandoconro/adminkanjicreator

任何建议将不胜感激,谢谢。

【问题讨论】:

  • 您能否分享您的私有路由组件,以便我们在解析身份验证状态时查看它的作用,以及您使用私有路由的代码?我怀疑你没有实际上有一个PrivateRoute 组件虽然通过你的代码sn-p 来判断。您还可以包括您的UserContext 代码吗?如果您在实施身份验证流程方面需要帮助,请查看docs 以获取身份验证工作流程示例。
  • @DrewReese 我更新了问题,并将此链接添加到完整的 repo gitlab.com/programandoconro/adminkanjicreator
  • auth 状态的正常值是多少?您可以从与“已验证”或“未验证”状态不同的初始状态开始,然后等待 not 的值等于初始状态。
  • @DrewReese 我添加了身份验证逻辑。谢谢您的建议。我会对此进行试验。

标签: reactjs typescript firebase routes react-router-dom


【解决方案1】:

我建议早点进行身份验证检查。所以像这样的东西,这样路由本身只有在 auth 中有东西时才会被渲染。我认为您的示例也缺少通常有帮助的 Switch 语句。

<Router>
    {!auth ? (
        <Switch>
            <Route exact path="/login" component={RedirectPage} />
        </Switch>
    ) : (
        <Switch>
            <Route exact path="/home" component={Home} />
            <Route exact path="/dashboard" component={Dashboard} />
        </Switch>
    )}
</Router>


【讨论】:

  • 我正在试验您的建议以使其发挥作用。谢谢。
【解决方案2】:

通常,您会希望某种“加载”或“不确定”状态表示既不经过身份验证也不表示未经身份验证。您可以使用这第三个“状态”来保存 UI,然后根据身份验证在任何事物上呈现一种或另一种方式。

由于您的 auth 逻辑解析为布尔值 true|false

const onAuthChange = (setState, setLoading) => {
  auth().onAuthStateChanged((u) => {
    if (!u) {
      console.log(u);
      setLoading(true);
    } else {
      setState(u);
      setLoading(false);
    }
  });
};

您可以使用初始auth 状态不是这两者的事实。我建议使用null

const [auth, setAuth] = useState<string | null>(null);

当使用auth 状态渲染Route 时,您可以增加逻辑以在决定重定向之前提前返回。

<Route
  path="/"
  render={() => {
    if (auth === null) return null;
    return <Redirect to={auth ? path : "/login" />;
  }}
/>

请注意,我也切换到了 render 属性,component 属性用于附加实际的 React 组件。这些处理方式略有不同。您可以阅读有关路由渲染方法的差异here

完整的路由器示例:

<Router>
  <Switch>
    <Route path="/home" component={Home} />
    <Route path="/dashboard" component={Dashboard} />
    <Route path="/login" component={RedirectPage} />
    <Route
      path="/"
      render={() => {
        if (auth === null) return null;
        return <Redirect to={auth ? path : "/login" />;
      }}
    />
  </Switch>
</Router>

请注意,我还包含了Switch 组件并重新排序了路由,因此在不太具体的路径之前列出了更具体的路径。这允许您从所有路由中删除不必要的 exact 属性,因为 Switch 仅呈现路由(相对于 Router 所做的包含性)。

【讨论】:

    【解决方案3】:

    我终于设法解决了这个问题。现在重新加载工作完美,安全性作为例外实施。这是我最后的Router

    <Router>
          <Route
            path="/"
            render={() =>
              logging ? <Redirect to={"/login"} /> : <Redirect to={path} />
            }
          />
          <Route exact path="/" render={() => auth && <Home />} />
          <Route exact path="/dashboard" render={() => auth && <Dashboard />} />
          <Route exact path="/login" component={Login} />
    </Router>
    
    

    这是组件现在的样子。

    import {
      Route,
      BrowserRouter as Router,
      Link,
      Redirect
    } from "react-router-dom";
    import { Container, Button } from "@material-ui/core/";
    import Login from "./Login";
    import { useContext, useState, useEffect } from "react";
    import { UserContext } from "../App";
    import { signOut } from "../Storage/Auth";
    
    const Routes = () => {
      const { auth, setAuth, logging } = useContext(UserContext);
      const handleSignOut = () => {
        signOut(setAuth);
        console.log("Auth", auth);
      };
      const pathname = window.location.pathname;
      const [path, setPath] = useState(pathname);
    
      useEffect(() => {
        console.log(path);
        path === "/login" && setPath("/");
        path !== "/" && path !== "/dashboard" && setPath("/");
      }, [auth]);
    
      const Home = () => {
        console.log("Home");
        return (
          <Container>
            <h1>Welcome</h1>
            <Link to="/">
              <Button onClick={handleSignOut}> Log Out</Button>
            </Link>
            <Link to="/dashboard">
              <Button> Dash</Button>
            </Link>
          </Container>
        );
      };
    
      const Dashboard = () => {
        console.log("Dash");
        return (
          <Container>
            <Link to="/">
              <Button> HOME</Button>
            </Link>
            <h1>Dashboard</h1>
          </Container>
        );
      };
    
      return (
        <Router>
          <Route
            path="/"
            render={() =>
              logging ? <Redirect to={"/login"} /> : <Redirect to={path} />
            }
          />
          <Route exact path="/" render={() => auth && <Home />} />
          <Route exact path="/dashboard" render={() => auth && <Dashboard />} />
          <Route exact path="/login" component={Login} />
        </Router>
      );
    };
    
    export { Routes };
    

    感谢@Richard 和@Drew 的支持。

    【讨论】:

      猜你喜欢
      • 2018-10-21
      • 2019-01-05
      • 1970-01-01
      • 2021-11-12
      • 2022-09-23
      • 2018-08-31
      • 2018-03-30
      • 1970-01-01
      • 2020-07-04
      相关资源
      最近更新 更多