2
votes

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)

2
Simple answer: you can't. Functional components cannot be async functionsMordechai
Do all your async logic in useEffect hookMordechai
I tried using use Effect but not sure where to place in the component.Prashant Gupta
Assuming you know how to use hooks, use it together with a state hook and update the state in the effect. In the rendering itself pass the state value, which can be a default value set in the state hook param.Mordechai
If you don't know about hooks, learn that firstMordechai

2 Answers

1
votes

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)
0
votes

Does the following work?

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