reactjsasynchronousreact-reduxreact-async

Async throws error in React Functional Component


I am trying to use use async in following functional component but throws error

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)

below is the verifyToken Function which return true or false

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
  }

above used refreshToken has a functional which gets response from an API call

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

Any suggestions would be grateful to fix this issue. If someone has better way to code this please let me know. I am bit new into ReactJS and open for suggestions. FIrst function is used to check on each route if Token is authorized or not. If its is authorized it allows to access page and if not redirects to Login Page.

After suggestions from @Mordechai, I have made below changes but it throws an error

./src/Router.js Line 193:40: React Hook "useState" is called in function "verified" which is neither a React function component or a custom React Hook function react-hooks/rules-of-hooks Line 194:3: React Hook "useEffect" is called in function "verified" which is neither a React function component or a custom React Hook function 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)


Solution

  • Hooks must be named useXxx otherwise eslint will complain. Also, hooks must be called on the top-level of a function component.

    You call verifyToken() in both the useState() default value param and in the effect. If it's a long process you should only do it in the effect.

    If you want to call verifyToken() in useVerify() just once in the lifetime of the component, you should add an empty array in the useEffect() dependency array

    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)