-1

I am making a create-react-app that stores an access token within document.cookie to handle login / accessing protected routes.

My main component, App.js has a function isLoggedIn():

    async isLoggedIn() {
        if (document.cookie !== '') {
            // Check for existence of `token` cookie
            let cookies = parseCookies(document.cookie);
            if (cookies.token) {
                try {
                    let payload = await BackendService.verifyToken(cookies.token);
                    this.setState({
                        'name': payload.name,
                        'email': payload.email
                    });
                    return payload.valid || false;
                } catch (err) {
                    return false;
                }
            } else {
                console.log('No token cookie found.')
                return false;
            }
        }
        return false;
    }

I pass this function as a prop to children to allow them to check login status:

<TopBar isLoggedIn={this.isLoggedIn} />

Within TopBar is where I conditionally render based on isLoggedIn():

                        { this.props.isLoggedIn() &&
                            <li className="nav-item">Welcome <a href="#account">{this.props.name}</a></li>
                        }
                        { !this.props.isLoggedIn() &&
                            <li className="navItem"><a href="#login" onClick={this.showModal}>Sign In</a></li>
                        }

However, when document.cookie is empty (deleted cookies through Application tab of Dev Tools), Welcome is rendered rather than the Sign In.

If I try to log the value of this.props.isLoggedIn() from within TopBar, instead of a boolean being printed, a Promise is printed? Which doesn't make sense to me. Sure async keyword on isLoggedIn() indicates a function could be returned, but I am explicitly returning a boolean at each stage of the control flow, so I am unsure how a promise is ever involved.

The only way I could see a promise being returned is because of the BackendService.verifyToken() function, but that is using await anyways and so shouldn't return a promise. Also a promise shouldn't be returned anyways if I immediately exit the function with false, right?

I have looked at similar questions, however like I said above, using the await keyword should be enough to return a value and not a promise.

  • It looks as though you aren't awaiting the promise anywhere? So `this.props.isLoggedIn()` at render is just a promise, so always truthy – OliverRadini Jul 13 '22 at 13:54
  • `let payload = await BackendService.verifyToken(cookies.token);` That's the only asynchronous operation happening –  Jul 13 '22 at 14:00
  • sure - but regardless, you'll be returning a promise. So when the render code evaluates `this.props.isLoggedIn()`, it'll get a promise, not a boolean. – OliverRadini Jul 13 '22 at 14:02
  • Isn't the whole point of the `await` keyword to indicate I want to wait for the asychronous operation to finish rather than dealing with promises? –  Jul 13 '22 at 14:03
  • I think that's maybe the part which is causing the confusion - if you define a function as `async` it'll always return a promise. – OliverRadini Jul 13 '22 at 14:04

2 Answers2

0

You're using a promise directly in the render rather than the actual boolean that you expect. Once you define a function as async, it'll always return a promise. Consider this code:

import React from "react";

const C1 = ({ isLoggedIn }) => (
  <div>I am{isLoggedIn() ? " " : " not "}logged in</div>
);
const C2 = ({ isLoggedIn }) => {
  const [loggedIn, setLoggedIn] = React.useState(false);

  React.useEffect(() => {
    isLoggedIn().then(setLoggedIn);
  }, [isLoggedIn]);

  return <div>I am{loggedIn ? " " : " not "}logged in</div>;
};

export default function App() {
  const isLoggedIn = async () => {
    console.log("Running check");
    return false;
  };

  return (
    <div>
      <C1 isLoggedIn={isLoggedIn} />
      <C2 isLoggedIn={isLoggedIn} />
    </div>
  );
}

C1 will render "I am logged in". C2 will render "I am not logged in".

OliverRadini
  • 6,238
  • 1
  • 21
  • 46
0

I actually solved it in a different way, since I am using Components and not Hooks:

Within my TopBar component, I use the componentDidMount() lifecycle hook:

async componentDidMount() {
    let result = await this.props.isLoggedIn();
    this.setState({'login' : result});
}

Then simply conditionally render based on state.login:

{ this.state.login &&