首页 > 解决方案 > 使用 react-router-v5 和 redux-toolkit 登录时重定向页面

问题描述

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

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

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

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

这是我在 express + mongo 中使用 do auth 的问题的一个小演示,即。异步还原。这没有按预期工作。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 存储库

标签: reactjsreduxreact-routerredux-thunkredux-toolkit

解决方案


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

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

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>
  )
}

第二:另一种方法可以用 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 上尝试您的逻辑。


推荐阅读