2

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)

8
  • Simple answer: you can't. Functional components cannot be async functions Commented Jul 8, 2020 at 14:00
  • Do all your async logic in useEffect hook Commented Jul 8, 2020 at 14:01
  • I tried using use Effect but not sure where to place in the component. Commented Jul 8, 2020 at 14:02
  • 1
    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. Commented Jul 8, 2020 at 14:04
  • If you don't know about hooks, learn that first Commented Jul 8, 2020 at 14:04

2 Answers 2

1

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)
Sign up to request clarification or add additional context in comments.

7 Comments

Unfortunately this also doesn't works as verified values goes every time undefined. Only thing No Syntax errors.. That's a good one. But verified value should also get read.
I have a problem.. AppRoute getscalled on each routes in application and verifyToken verifies accessToken and changes it every time in state. To make use of it I placed "auth.values" inside empty array of useEffect but it started calling verifyToken n number of times and went in loop. Please help.
Passing the auth.values in the array is correct. If the parent component creates the object anew for each rerender, checkout the built in hook useMemo to memorize the object and only change it when needed.
Thank for immediate reply, I have solved this problem by using path as every page gets loaded then only i need these checks and AppRoute used to get called. So i used in empty array as path. Thanks alot.. Because of you my 3 days frustration got ended. I have one more problem but i will post it as a new questions soon
Also let me know what this means React Hook useEffect has a missing dependency: 'auth.values'. Either include it or remove the dependency array. If 'setVerifiedValue' needs the current value of 'auth', you can also switch to useReducer instead of useState and read 'auth.values' in the reducer
|
0

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

1 Comment

No it still throws an error ./src/Router.js Line 198:39: React Hook "React.useState" cannot be called inside a callback. React Hooks must be called in a React function component or a custom React Hook function react-hooks/rules-of-hooks Line 199:7: React Hook "React.useEffect" cannot be called inside a callback. React Hooks must be called in a React function component or a custom React Hook function react-hooks/rules-of-hooks

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.