【问题标题】:Private route (react gatsby) render before able to retrieve user information for authentication私有路由(react gatsby)在能够检索用户信息进行身份验证之前呈现
【发布时间】:2021-03-07 04:54:09
【问题描述】:

验证用户令牌后,我会将用户信息设置到 localStorage。 “isLoggedIn()”函数将检索用户信息以决定用户是否应该继续。

我遇到的问题是私有路由会在信息设置到localStorage之前先渲染,所以“isLoggedIn()”函数会先返回空的localStorage,我必须重新加载/刷新才能成功登录。

下面是我的代码片段,这个项目对我来说很重要,我必须尽快完成它。我希望我能得到任何人的帮助。提前谢谢你们。

Index.js

const Index = () => {

  return (
    <div>
      <Router>
        <PrivateRoute path="/" component={Dashboard} />
        <PrivateRoute path="/upload" component={Upload} />
        <PrivateRoute path="/settings" component={Settings} />
        <PrivateRoute path="/agents" component={Agents} />
        <PrivateRoute path="/statistics" component={Statistics} />
        <Login path="/login" />
      </Router>
    </div>
  )

}

PrivateRoute.js

const PrivateRoute = ({ component: Component, location, ...rest }) => {

  if (!isLoggedIn() && location.pathname !== `/login`) {
    navigate("/login")
    return null
  }
  return <Component {...rest} />
}

Auth.js

export const isBrowser = () => typeof window !== "undefined"

export const getUser = () =>
  isBrowser() && window.localStorage.getItem("user")
    ? JSON.parse(window.localStorage.getItem("user"))
    : {}

export const setUser = user =>
  window.localStorage.setItem("user", JSON.stringify(user))


export const handleLogin = async ({ email, password }) => {

  const loginUser = { email, password };
  const loginResponse = await Axios.post("http://localhost:5000/api/users/login", loginUser);
  if (loginResponse) {
    localStorage.setItem("auth-token", loginResponse.data.token);

    let token = localStorage.getItem("auth-token");
    if (token === null) {
      localStorage.setItem("auth-token", "");
      token = "";
    }
    const tokenResponse = await Axios.post('http://localhost:5000/api/users/tokenIsValid', null, { headers: { "x-auth-token": token } });
    if (tokenResponse.data) {
      const userRes = await Axios.get("http://localhost:5000/api/users/", {
        headers: { "x-auth-token": token },
      });
      
      return setUser({
        username: userRes.data.displayName
      })
    }
  }

  return false
}

export const isLoggedIn = () => {

  const user = getUser();
  return !!user.username;

}

export const logout = callback => {
  setUser({})
  localStorage.setItem("auth-token", "");
  callback()
}

loginComponent.js

class loginComponent extends React.Component {
  state = {
    email: "",
    password: "",
    //sessionStay: "false"
  }

  handleSubmit = event => {
    event.preventDefault()
    handleLogin(this.state)
   
  }
  render() {
    if (isLoggedIn()) {
      if (typeof window !== "undefined") {
        navigate(`/`)
      }
    }

    return (
      <div className="login-component-wrapper">
        <img src={logo} className="login-logo" alt="logo" />
        <h2>Agent Reporting Dashboard</h2>
        <form className="login-form">
          <div className="login-form-wrapper">
            <label className="form-field-label" htmlFor="email">
              Email:{" "}
            </label>
            <input
              className="form-field"
              name="email"
              type="email"
              onChange={e => {
                this.setState({ email: e.target.value })
              }}
              onKeyPress={e => {
                if (e.key === "Enter") {
                  this.handleSubmit(e)
                  navigate(`/`)
                }
              }}
              value={this.state.email}
            />
            <label className="form-field-label" htmlFor="password">
              Password:{" "}
            </label>
            <input
              className="form-field"
              name="password"
              type="password"
              onChange={e => {
                this.setState({ password: e.target.value })
              }}
              onKeyPress={e => {
                if (e.key === "Enter") {
                  this.handleSubmit(e)
                  navigate(`/`)
                }
              }}
              value={this.state.password}
            />
            <input
              type="checkbox"
              name="session-stay"
              onChange={e => {
                this.setState({ sessionStay: e.target.value })
              }}
              value="true"
            />
            <label className="form-field-label" htmlFor="session-stay">
              Keep me logged in
            </label>
            <br />
            <button
              type="button"
              className="login-button btn-primary"
              onClick={event => {
                this.handleSubmit(event)
                navigate(`/`)
              }}
            >
              Login
            </button>
          </div>
        </form>
      </div>
    )
  }
}

【问题讨论】:

    标签: reactjs authentication jwt gatsby mern


    【解决方案1】:

    磁盘操作(如设置localStorage)是异步的,因为它需要一些时间才能执行,具体取决于 PC、网络等。

    如果没有 CodeSandbox,很难猜测代码的行为方式,但对我来说,这似乎是一个异步问题。您可以尝试添加以下内容:

    export const handleLogin = async ({ email, password }) => {
    
      const loginUser = { email, password };
      const loginResponse = await Axios.post("http://localhost:5000/api/users/login", loginUser);
      if (loginResponse) {
        await localStorage.setItem("auth-token", loginResponse.data.token);
    
        let token = await localStorage.getItem("auth-token");
        if (token === null) {
          await  localStorage.setItem("auth-token", "");
          token = "";
        }
        const tokenResponse = await Axios.post('http://localhost:5000/api/users/tokenIsValid', null, { headers: { "x-auth-token": token } });
        if (tokenResponse.data) {
          const userRes = await Axios.get("http://localhost:5000/api/users/", {
            headers: { "x-auth-token": token },
          });
          
          return setUser({
            username: userRes.data.displayName
          })
        }
      }
    
      return false
    }
    

    注意异步handleLogin 函数中的await

    【讨论】:

    • 我个人也觉得是异步的问题,但是后来我试了你的方法还是不行,私有路由还是先通过获取空的localStorage渲染,然后用户信息才去了进入localStorage,所以当我刷新时,私有路由再次呈现,这次它检测到localStorage中的用户信息。感谢您的帮助,还有其他方法可以解决吗?我尽量避免使用 setTimeOut
    • 我会尝试添加 setTimeout 方法以获取导致此行为的函数,只需调试即可。一旦你检测到它,我会尝试用一个优雅的解决方法来修复
    • 是的,我确实尝试将 setTimeout 方法放在 useEffect 挂钩中,然后我将我的 PrivateRoute.js 中的整个 if 语句放在 setTimeout 函数中,在那里我将它延迟 500 毫秒,并且在它加载时我显示加载屏幕。这样做效果很好,所以我想问题是私有路由将首先呈现,并且“isLoggedIn()”函数将在本地存储中设置用户信息之前运行,但直到现在我仍然找不到解决它的方法
    猜你喜欢
    • 2021-04-08
    • 1970-01-01
    • 2018-02-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-24
    相关资源
    最近更新 更多