登录时使用 react-router-v5 和 redux-toolkit 重定向页面 [英] Redirect page upon login using react-router-v5 and redux-toolkit

查看:41
本文介绍了登录时使用 react-router-v5 和 redux-toolkit 重定向页面的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用的是 react-router-dom v5.2.

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

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

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

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

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

<小时>

这是App.js:

const ProtectedRoute = ({ component: Component, ...rest }) =>{const authState = useSelector(selectorAuth)//const location = useLocation()返回 (<路线{...休息}渲染={道具=>{如果(authState.isUserLoggedIn){return <Component {...props}/>} 别的 {返回 (<重定向到={{路径名:/",状态: {来自:props.location,},}}/>)}}}/>)}const App = () =>{返回 (<路由器><div tw="flex flex-col bg-green-100 min-h-screen"><导航栏/><开关><路由精确路径="/" component={Landing}/><ProtectedRoute path="/home" component={Home}/><ProtectedRoute path="/explore" component={Explore}/><Route path="*" component={() =>404 未找到."}/></开关>

</路由器>)}

这里是 ModalLogin.js.

const ModalLogin = props =>{const { loginModalBool, setLoginModalBool } = propsconst [username, setUsername] = useState("")const [密码,setPassword] = useState("")const dispatch = useDispatch()const 历史 = useHistory()const 尝试登录 = e =>{e.preventDefault()调度(尝试登录(用户名,密码))history.push("/home")}返回 (<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">登录</p><button tw="text-sm" onClick={() =>setLoginModalBool(!loginModalBool)}>关闭

<div tw="flex flex-col justify-around my-1"><form onSubmit={attemptLogin} tw=""><输入tw="我的-1"值={用户名}onChange={e =>设置用户名(e.target.value)}占位符=用户名"/><输入tw="我的-1"值={密码}onChange={e =>setPassword(e.target.value)}类型=密码"占位符=密码"/><按钮类型=提交"tw="my-1 p-1 圆角 bg-gray-800 text-gray-100 悬停:bg-gray-900">登录</表单>

)}

这是 authSlice.js.

import { createSlice } from "@reduxjs/toolkit"从axios"导入 axios常量初始状态 = {isUserLoggedIn: 假,用户名: "",}导出 const authSlice = createSlice({名称:授权",初始状态:初始状态,减速器:{登录:(状态,动作)=>{const 用户 = action.payloadif (!user) return alert("登录失败.用户名或密码不正确.")状态.用户名 = 用户.用户名state.isUserLoggedIn = true},注销:(状态,动作)=>{//window.localStorage.removeItem("loggedInUser")state.username = ""state.isUserLoggedIn = false},注册:(状态,动作)=>{const 用户 = action.payloadstate.username = user.data.usernamestate.isUserLoggedIn = true},},})export const tryLogin = (username, password) =>{返回异步调度 =>{尝试 {const response = await axios.post("/api/auth/login", {用户名:用户名,密码:密码,})常量用户 = {令牌: response.headers["auth-token"],用户名:response.data.username,}//window.localStorage.setItem("token", response.headers["auth-token"])调度(登录(用户))}赶上(e){alert("用户名/密码不正确.")}}}export const selectorAuth = state =>状态.auth导出 const { 登录,注销 } = authSlice.actions导出默认 authSlice.reducer

<小时>

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

这是 Github 存储库

解决方案

你的代码没有定义登录后的重定向逻辑.您可以通过两种方式实现.

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

const AuthRoute = ({ component: Component, ...rest }) =>{const authState = useSelector(selectorAuth)const location = useLocation()返回 (<路线{...休息}渲染={道具=>{如果(!authState.isUserLoggedIn){return <Component {...props}/>} 别的 {返回 (<重定向到={{路径名:/home",状态: {来自:位置,},}}/>)}}}/>)}const App = () =>{返回 (<路由器><div tw="flex flex-col bg-green-100 min-h-screen"><导航栏/><开关>//用于登录用户重定向到首页<AuthRoute 精确路径="/" component={Landing}/><ProtectedRoute path="/home" component={Home}/><ProtectedRoute path="/explore" component={Explore}/><Route path="*" component={() =>404 未找到."}/></开关>

</路由器>)}

2nd :另一种方法可以使用 history.push() 或 history.replace() 进行命令式处理:

const Layout = () =>{const authState = useSelector(selectorAuth);const 历史 = useHistory();useEffect(() => {//如果 isUserLoggedIn 变成 true 重定向到/home如果(authState.isUserLoggedIn){history.push("/home");}}, [authState.isUserLoggedIn]);//当 isUserLoggedIn 改变时触发返回 (<开关><路由精确路径="/" component={Landing}/><ProtectedRoute path="/home" component={Home}/><ProtectedRoute path="/explore" component={Explore}/><Route path="*" component={() =>404 未找到."}/></开关>);};const App = () =>{返回 (<路由器><div tw="flex flex-col bg-green-100 min-h-screen"><导航栏/><布局/>

</路由器>);};

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

 <路由精确路径="/" component={Landing}/><ProtectedRoute path="/home" component={Home}/><ProtectedRoute path="/explore" component={Explore}/><Route path="*" component={() =>404 未找到."}/>

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

回到你的案例;当您登录时,您没有离开/"路径.因为没有实现离开/"路径的逻辑.因此,即使经过身份验证,它也会再次与登录页面匹配.它与路线路径(着陆页)匹配并停留在那里.它不会继续并在 ProtectedRoute 上尝试您的逻辑.

I'm using react-router-dom v5.2.

Upon login, I want my page to redirect to /home from /. The login form is at /.

When I try to perform authentication without any async function (ie. comparing username and password to hardcoded values in react), everything works perfectly.

But when I do perform authentication using express and mongo, the redirection upon login stops working. If I login again, then the redirection happens. Protected routes still work (redirect to login page if the user is not logged in).

Here's a small demo of the problem where I use do auth with express + mongo ie. async redux. This is not working as intended. https://youtu.be/Zxm5GOYymZQ

Here's the link of the app where I use hardcoded username and password (both "test") to do auth. No async here. This works as intended. Username and password are both "test". https://poke-zoo.herokuapp.com/


Here's 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>
  )
}

Here's 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>
  )
}

Here's the 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


Am I using react-router with redux-toolkit incorrectly?

Here's the Github repo

解决方案

Your code does not define redirection logic after login. You can do it in two way.

1st : You can define another redirection wrapper for authentication if you want your routes to redirect in case of authentication.

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 : Another approach can be imperatively handling with history.push() or 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>
  );
};

Why didn't your code work? Take a look at the code below :

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

What it does? It checks your browser path and check if it matches with given Route rule from top to down. If Route path matches then it renders the component if not it continues through down visiting each one of Route until it matches your 404.

So back to your case; when you login, you were not leaving "/" path. Because there is no logic implemented to leave the "/" path. So it again match with landing page even though it is authenticated. It matches with route path (Landing page) and stays there. It does not continue and try your logic on ProtectedRoute.

这篇关于登录时使用 react-router-v5 和 redux-toolkit 重定向页面的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
其他开发最新文章
热门教程
热门工具
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆