【问题标题】:Async throws error in React Functional Component异步在 React 功能组件中引发错误
【发布时间】:2020-07-08 13:46:39
【问题描述】:

我正在尝试在以下功能组件中使用异步但抛出错误

const RouteConfig =  async ({ component: Component, fullLayout, user, auth, ...rest}) => (

  <Route
    {...rest} 
    render={props => {
      
      return (
        <ContextLayout.Consumer>
          {context => {
            let LayoutTag =
              fullLayout === true
                ? context.fullLayout
                : context.state.activeLayout === 'horizontal'
                ? context.horizontalLayout
                : context.VerticalLayout
                const verified = await verifyToken(auth.values)
            return  (auth.values !== undefined && auth.values.isSignedIn && verified) ? (
              <LayoutTag {...props} permission='{user}'>
                <Suspense fallback={<Spinner />}>
                  <Component {...props}></Component>
                </Suspense>
              </LayoutTag>
            ) : (
              <context.fullLayout {...props} permission={user}>
                <Suspense fallback={<Spinner />}>
                  <Login {...props} />
                </Suspense>
              </context.fullLayout>
            )
          }}
        </ContextLayout.Consumer>
      )
    }}
  />
)
const mapStateToProps = state => {
  return {
    user: state.auth.login.userRole,
    auth: state.auth.login
  }
}

const AppRoute = connect(mapStateToProps)(RouteConfig)

下面是返回真假的verifyToken函数

const verifyToken = async props => {
    if (props.accessToken !== undefined) {
    //if (props !== undefined) {
        if (assertAlive(jwt.decode(props.accessToken))) {
            const verified = await refreshToken(props)
            console.log(verified)
            if (verified){
                console.log('Authorized')
                return true
            } else {
                console.log('Unauthorized')
                return false
            }
        } else {
            return false
        }
    }else 
        return false
}

function assertAlive (decoded) {
    const now = Date.now().valueOf() / 1000
    if (typeof decoded.exp !== 'undefined' && decoded.exp < now) {
      //throw new Error(`token expired: ${JSON.stringify(decoded)}`)
      return false
    }
    if (typeof decoded.nbf !== 'undefined' && decoded.nbf > now) {
      //throw new Error(`token not yet valid: ${JSON.stringify(decoded)}`)
      return false
    }
    return true
  }

上面使用的 refreshToken 有一个从 API 调用中获取响应的函数

export const  refreshToken =  async  () => {
  const options = { withCredentials: true };
  const resp = await axios.post('http://localhost:4000/api/auth/verifyToken',{}, options).catch(err => {console.log(err); return false;});
  //console.log(resp.status)
    if (resp.status === 200){
      //console.log('200 passed')
      return true
    }else if (resp.status === 401){
      //console.log('401 failed')
      return false
    }else{
      //console.log('204 failed')
      return false
    }
}

如果有任何建议可以解决此问题,我们将不胜感激。如果有人有更好的编码方法,请告诉我。我对 ReactJS 有点陌生,并愿意接受建议。 FIrst 函数用于检查每个路由是否 Token 被授权。如果它被授权它允许访问页面,如果没有重定向到登录页面。

根据@Mordechai 的建议,我进行了以下更改,但会引发错误

./src/Router.js 第 193:40 行:React Hook "useState" 在函数 "verified" 中被调用,该函数既不是 React 函数组件也不是自定义 React Hook 函数 react-hooks/rules-of-hooks 第 194:3 行:React Hook "useEffect" 在函数 "verified" 中被调用,该函数既不是 React 函数组件也不是自定义 React Hook 函数 react-hooks/rules-of-hooks

function verified(auth){
  const [verified, setVerifiedValue] = useState(verifyToken(auth.values));
  useEffect(() => { setVerifiedValue(verifyToken(auth.values) )})
  return verified;
}

const RouteConfig =  ({ component: Component, fullLayout, user, auth, ...rest}) => (

  <Route
    {...rest} 
    render={props => {
      
      //useEffect(() => {const verified = verifyToken(auth.values) });
      return (
        <ContextLayout.Consumer>
          {context => {
            let LayoutTag =
              fullLayout === true
                ? context.fullLayout
                : context.state.activeLayout === 'horizontal'
                ? context.horizontalLayout
                : context.VerticalLayout
                console.log(VerifiedToken)
            return  (auth.values !== undefined && auth.values.isSignedIn && VerifiedToken) ? (
              <LayoutTag {...props} permission='{user}'>
                <Suspense fallback={<Spinner />}>
                  <Component {...props}></Component>
                </Suspense>
              </LayoutTag>
            ) : (
              <context.fullLayout {...props} permission={user}>
                <Suspense fallback={<Spinner />}>
                  <Login {...props} />
                </Suspense>
              </context.fullLayout>
            )
          }}
        </ContextLayout.Consumer>
      )
    }}
  />
)
const mapStateToProps = state => {
  return {
    user: state.auth.login.userRole,
    auth: state.auth.login
  }
}

const AppRoute = connect(mapStateToProps)(RouteConfig)
const VerifiedToken = connect(mapStateToProps)(verified)

【问题讨论】:

  • 简单的答案:你不能。函数式组件不能是异步函数
  • useEffect钩子里做你所有的异步逻辑
  • 我尝试使用 use Effect 但不确定在组件中放置的位置。
  • 假设您知道如何使用钩子,将它与状态钩子一起使用并更新效果中的状态。在渲染本身传递状态值,可以是状态钩子参数中设置的默认值。
  • 如果你不了解 hooks,请先学习

标签: reactjs asynchronous react-redux react-async


【解决方案1】:

Hooks 必须命名为 useXxx 否则 eslint 会报错。此外,钩子必须在函数组件的顶层调用。

您在useState() 默认值参数和效果中都调用了verifyToken()。如果这是一个漫长的过程,您应该只在效果中进行。

如果你想在组件的生命周期内只在useVerify()中调用verifyToken()一次,你应该在useEffect()依赖数组中添加一个空数组

function useVerified(auth){
  const [verified, setVerifiedValue] = useState();
  useEffect(() => {
    const doVerify = async () => {
      setVerifiedValue(await verifyToken(auth.values))
    }
    doVerify()
  }, [])
  return verified;
}

const RouteConfig =  ({ component: Component, fullLayout, user, auth, ...rest}) => {

  const verified = useVerified(auth);

  return <Route
    {...rest} 
    render={props => {
      return (
        <ContextLayout.Consumer>
          {context => {
            let LayoutTag =
              fullLayout === true
                ? context.fullLayout
                : context.state.activeLayout === 'horizontal'
                ? context.horizontalLayout
                : context.VerticalLayout
                console.log(VerifiedToken)
            return  (auth.values !== undefined && auth.values.isSignedIn && VerifiedToken) ? (
              <LayoutTag {...props} permission='{user}'>
                <Suspense fallback={<Spinner />}>
                  <Component {...props}></Component>
                </Suspense>
              </LayoutTag>
            ) : (
              <context.fullLayout {...props} permission={user}>
                <Suspense fallback={<Spinner />}>
                  <Login {...props} />
                </Suspense>
              </context.fullLayout>
            )
          }}
        </ContextLayout.Consumer>
      )
    }}
  />
}
const mapStateToProps = state => {
  return {
    user: state.auth.login.userRole,
    auth: state.auth.login
  }
}

const AppRoute = connect(mapStateToProps)(RouteConfig)
const VerifiedToken = connect(mapStateToProps)(verified)

【讨论】:

  • 不幸的是,这也不起作用,因为每次未定义时验证的值都会消失。只有一件事没有语法错误.. 这是一个很好的。但验证值也应该被读取。
  • 我有一个问题.. AppRoute 在应用程序中的每个路由上被调用,verifyToken 验证 accessToken 并在每次状态时更改它。为了使用它,我将“auth.values”放在 useEffect 的空数组中,但它开始调用 verifyToken n 次并进入循环。请帮忙。
  • 传递数组中的 auth.values 是正确的。如果父组件为每次重新渲染重新创建对象,请签出内置挂钩useMemo 以记住该对象并仅在需要时更改它。
  • 感谢您的即时回复,我已经通过使用路径解决了这个问题,因为每个页面都被加载,然后我只需要这些检查和 AppRoute 用于被调用。所以我用空数组作为路径。非常感谢..因为你,我三天的挫败感结束了。我还有一个问题,但我会尽快将其作为新问题发布
  • 另外让我知道这意味着什么React Hook useEffect 缺少依赖项:'auth.values'。包括它或删除依赖数组。如果'setVerifiedValue'需要'auth'的当前值,你也可以切换到useReducer而不是useState,并在reducer中读取'auth.values'
【解决方案2】:

以下是否有效?

const NONE = {};
const RouteConfig = ({
  component: Component,
  fullLayout,
  user,
  auth,
  ...rest
}) => (
  <Route
    {...rest}
    render={(props) => {
      const [verified, setVerified] = React.useState(NONE);
      React.useEffect(
        () => verifyToken(auth.values).then(setVerified),
        []
      );
      if (verified === NONE) {
        return null;
      }
      return (
        <ContextLayout.Consumer>
          {(context) => {
            let LayoutTag =
              fullLayout === true
                ? context.fullLayout
                : context.state.activeLayout ===
                  'horizontal'
                ? context.horizontalLayout
                : context.VerticalLayout;
            return auth.values !== undefined &&
              auth.values.isSignedIn &&
              verified ? (
              <LayoutTag {...props} permission="{user}">
                <Suspense fallback={<Spinner />}>
                  <Component {...props}></Component>
                </Suspense>
              </LayoutTag>
            ) : (
              <context.fullLayout
                {...props}
                permission={user}
              >
                <Suspense fallback={<Spinner />}>
                  <Login {...props} />
                </Suspense>
              </context.fullLayout>
            );
          }}
        </ContextLayout.Consumer>
      );
    }}
  />
);

【讨论】:

  • 不,它仍然会引发错误。/src/Router.js 第 198:39 行:无法在回调中调用 React Hook "React.useState"。必须在 React 函数组件或自定义 React Hook 函数 react-hooks/rules-of-hooks 中调用 React Hooks 第 199:7 行:不能在回调中调用 React Hook “React.useEffect”。 React Hooks 必须在 React 函数组件或自定义 React Hook 函数中调用 react-hooks/rules-of-hooks
猜你喜欢
  • 2021-05-25
  • 1970-01-01
  • 2020-05-06
  • 1970-01-01
  • 2020-10-13
  • 1970-01-01
  • 2023-03-20
  • 2020-08-04
  • 2020-06-30
相关资源
最近更新 更多