使用 react-router-v5 和 redux-toolkit 登录时重定向页面

Posted

技术标签:

【中文标题】使用 react-router-v5 和 redux-toolkit 登录时重定向页面【英文标题】:Redirect page upon login using react-router-v5 and redux-toolkit 【发布时间】:2020-09-21 01:31:29 【问题描述】:

我正在使用 react-router-dom v5.2。

登录后,我希望我的页面从/ 重定向到/home。登录表单位于/

当我尝试在没有任何异步功能的情况下执行身份验证(即,将用户名和密码与反应中的硬编码值进行比较)时,一切正常。

但是当我使用 express 和 mongo 执行身份验证时,登录时的重定向停止工作。如果我再次登录,则会发生重定向。受保护的路由仍然有效(如果用户未登录,则重定向到登录页面)。

这是一个关于我使用 do auth 和 express + mongo 的问题的小演示,即。异步还原。这没有按预期工作。 https://youtu.be/Zxm5GOYymZQ

这是我使用硬编码的用户名和密码(均为“测试”)进行身份验证的应用程序的链接。这里没有异步。这按预期工作。用户名和密码都是“测试”。 https://poke-zoo.herokuapp.com/


这里是App.js

const ProtectedRoute = ( component: Component, ...rest ) => 
  const authState = useSelector(selectorAuth)
  // const location = useLocation()
  return (
    <Route
      ...rest
      render=props => 
        if (authState.isUserLoggedIn) 
          return <Component ...props />
         else 
          return (
            <Redirect
              to=
                pathname: "/",
                state: 
                  from: props.location,
                ,
              
            />
          )
        
      
    />
  )


const App = () => 
  return (
    <Router>
      <div tw="flex flex-col bg-green-100 min-h-screen">
        <Navbar />
        <Switch>
          <Route exact path="/" component=Landing />
          <ProtectedRoute path="/home" component=Home />
          <ProtectedRoute path="/explore" component=Explore />
          <Route path="*" component=() => "404 Not found." />
        </Switch>
      </div>
    </Router>
  )

这里是ModalLogin.js

const ModalLogin = props => 
  const  loginModalBool, setLoginModalBool  = props
  const [username, setUsername] = useState("")
  const [password, setPassword] = useState("")

  const dispatch = useDispatch()
  const history = useHistory()

  const attemptLogin = e => 
    e.preventDefault()
    dispatch(tryLogin(username, password))
    history.push("/home")
  

  return (
    <div tw="flex flex-col text-center h-full w-64 bg-gray-200 text-gray-900 rounded-lg shadow-lg p-2 md:p-4 lg:p-6">
      <div tw="flex flex-row justify-between">
        <p tw="text-lg">Login</p>
        <button tw="text-sm" onClick=() => setLoginModalBool(!loginModalBool)>
          close
        </button>
      </div>
      <div tw="flex flex-col justify-around my-1">
        <form onSubmit=attemptLogin tw="">
          <input
            tw="my-1"
            value=username
            onChange=e => setUsername(e.target.value)
            placeholder="username"
          />
          <input
            tw="my-1"
            value=password
            onChange=e => setPassword(e.target.value)
            type="password"
            placeholder="password"
          />
          <button
            type="submit"
            tw="my-1 p-1 rounded bg-gray-800 text-gray-100 hover:bg-gray-900"
          >
            log in
          </button>
        </form>
      </div>
    </div>
  )

这是authSlice.js

import  createSlice  from "@reduxjs/toolkit"
import axios from "axios"

const initialState = 
  isUserLoggedIn: false,
  username: "",


export const authSlice = createSlice(
  name: "auth",
  initialState: initialState,
  reducers: 
    login: (state, action) => 
      const user = action.payload

      if (!user) return alert("Login failed. Incorrect username or password.")

      state.username = user.username
      state.isUserLoggedIn = true
    ,
    logout: (state, action) => 
      // window.localStorage.removeItem("loggedInUser")
      state.username = ""
      state.isUserLoggedIn = false
    ,
    signup: (state, action) => 
      const user = action.payload
      state.username = user.data.username
      state.isUserLoggedIn = true
    ,
  ,
)

export const tryLogin = (username, password) => 
  return async dispatch => 
    try 
      const response = await axios.post("/api/auth/login", 
        username: username,
        password: password,
      )

      const user = 
        token: response.headers["auth-token"],
        username: response.data.username,
      

      // window.localStorage.setItem("token", response.headers["auth-token"])

      dispatch(login(user))
     catch (e) 
      alert("Incorrect Username/Password.")
    
  


export const selectorAuth = state => state.auth
export const  login, logout  = authSlice.actions
export default authSlice.reducer


我是否错误地将 react-router 与 redux-toolkit 一起使用?

这是Github repo

【问题讨论】:

Horeku 链接对我有效,禁用您的扩展 是的,heroku 链接有效。因为它不包含使用 express 和 mongo 的异步身份验证。但是当使用 express 和 mongo 时(如视频所示),重定向停止工作。 我的错,我读得很快:D 我已经克隆并修复了你的代码。您可以尝试让我知道它是否有效。 【参考方案1】:

您的代码在登录后没有定义重定向逻辑。你可以通过两种方式做到这一点。

第一个:如果您希望路由在身份验证时重定向,您可以定义另一个重定向包装器进行身份验证。

const AuthRoute = ( component: Component, ...rest ) => 
  const authState = useSelector(selectorAuth)
  const location = useLocation()
  return (
    <Route
      ...rest
      render=props => 
        if (!authState.isUserLoggedIn) 
          return <Component ...props />
         else 
          return (
            <Redirect
              to=
                pathname: "/home",
                state: 
                  from: location,
                ,
              
            />
          )
        
      
    />
  )


const App = () => 
  return (
    <Router>
      <div tw="flex flex-col bg-green-100 min-h-screen">
        <Navbar />
        <Switch>
          // It is for login users to redirect to home page
          <AuthRoute exact path="/" component=Landing />
          <ProtectedRoute path="/home" component=Home />
          <ProtectedRoute path="/explore" component=Explore />
          <Route path="*" component=() => "404 Not found." />
        </Switch>
      </div>
    </Router>
  )

2nd :另一种方法可以使用 history.push() 或 history.replace() 强制处理:

const Layout = () => 
  const authState = useSelector(selectorAuth);
  const history = useHistory();

  useEffect(() => 
    // if isUserLoggedIn turned to true redirect to /home
    if (authState.isUserLoggedIn)  
      history.push("/home");
    
  , [authState.isUserLoggedIn]); // triggers when isUserLoggedIn changes

  return (
    <Switch>
      <Route exact path="/" component=Landing />
      <ProtectedRoute path="/home" component=Home />
      <ProtectedRoute path="/explore" component=Explore />
      <Route path="*" component=() => "404 Not found." />
    </Switch>
  );
;

const App = () => 
  return (
    <Router>
      <div tw="flex flex-col bg-green-100 min-h-screen">
        <Navbar />
        <Layout />
      </div>
    </Router>
  );
;

为什么你的代码不起作用?看看下面的代码:

      <Route exact path="/" component=Landing />
      <ProtectedRoute path="/home" component=Home />
      <ProtectedRoute path="/explore" component=Explore />
      <Route path="*" component=() => "404 Not found." />

它有什么作用? 它会检查您的浏览器路径并检查它是否与给定的路由规则从上到下匹配。如果 Route 路径匹配,则它会渲染组件,否则它会继续向下访问每个 Route,直到它与您的 404 匹配。

回到你的情况;当您登录时,您没有离开“/”路径。因为没有实现离开“/”路径的逻辑。因此,即使它已通过身份验证,它也会再次与登录页面匹配。它与路由路径(登陆页面)匹配并停留在那里。它不会继续并在 ProtectedRoute 上尝试您的逻辑。

【讨论】:

感谢您的回答。现在可以了! :D 但我真的不明白为什么它现在起作用了我之前做错了什么?你能再解释一下吗? React-router 真的让我很困惑(没有很多地方可以解释实际发生的事情)。另外,你能解释一下(用代码)第二种方法吗?在我卡住之前,我一直在按照您提到的链接中的代码进行操作。【参考方案2】:

当前接受的答案解决了您的问题,但无法正确确定您遇到问题的原因。所以我想为你(以及其他阅读本文的人)解释一下。

问题:重定向太快

const attemptLogin = e => 
  e.preventDefault()
  dispatch(tryLogin(username, password))
  history.push("/home")

此代码调度tryLogin立即 调用history.push("/home")。它不会等待分派的操作完成并更新存储。

当您调用history.push("/home") 时,您的应用会为组件Home 加载ProtectedRoute。在ProtectedRoute 中,您使用选择器检查authState.isUserLoggedIn 的值。如果是false,则重定向到"/"

因此,如果您在 authState.isUserLoggedIn 的值更新之前重定向到 "/home",那么您最终将被重定向回 "/",而不是能够查看 Home 组件。

这就是您在使用异步身份验证时遇到这些问题的原因:

当我尝试在没有任何异步功能的情况下执行身份验证(即,将用户名和密码与反应中的硬编码值进行比较)时,一切正常。

但是当我使用 express 和 mongo 执行身份验证时,登录时的重定向停止工作。

解决方案:重定向前等待

您需要等待authState 更新之前重定向到受保护的路由。

有很多方法可以做到这一点,但通常我们希望使用选择器来监听发布到 redux 状态的更改。这是一种使用ModalLogin 有条件地渲染Redirect 组件的方法。我还建议包含某种登录错误选择器,以防调度完成解决但登录不成功。您可能希望在模式中向用户显示错误消息。

const ModalLogin = props => 
  const  loginModalBool, setLoginModalBool  = props
  const [username, setUsername] = useState("")
  const [password, setPassword] = useState("")

  const dispatch = useDispatch()
  const history = useHistory()
  
  // Look at the authState from redux.  This value will automatically update.
  const authState = useSelector(selectorAuth)

  const attemptLogin = e => 
    e.preventDefault()
    dispatch(tryLogin(username, password))
    // don't redirect here anymore
  

  // Once logged in, render the redirection
  if ( authState.isUserLoggedIn ) 
     return (
       <Redirect to="/home" />
     );
  
  
  // Otherwise, render the modal
  return (
    /* your current code */
  )

【讨论】:

【参考方案3】:

老实说,我所做的是在用户登录时使用普通的旧 javascript 来更改位置

window.location = "/redirect"

【讨论】:

我应该把上面的文件放在哪里?模态登录.js?任何反应路由器的方式来做到这一点? 谢谢,有时最简单的方法是最好的方法...

以上是关于使用 react-router-v5 和 redux-toolkit 登录时重定向页面的主要内容,如果未能解决你的问题,请参考以下文章

在 react-router-v4 中使用 history.push in action creator?

react-router-v4 没有正确重定向

React-router-v6 嵌套路由只渲染主路由

cisco4507引擎模式切换

5 Ways to Make Your Hive Queries Run Faster

20170711_map/reduce