1

In my React JS project, I have a RequireAuth.js which is used to check if the user is authorized every time they change pages. As an example, inside the const "confirmToken", I'm calling an async function that returns true if the user's token is valid or false.

const confirmToken = async () => {
    return await check();
  };

var v = confirmToken().then((result) => {
    return result;
});

It's working. But the variable v is returning as Promise.

Promise {[[PromiseState]]: 'pending', [[PromiseResult]]: undefined}

I've checked other similar questions on Stack, but I still don't know what I'm getting wrong here. Is there no way to return the boolean in a new variable? My code is like this. The allowedRoles is a list of user levels that are passed in the route with RequireAuth.

const RequireAuth = ({ allowedRoles }) => {
  const location = useLocation();

  try {
    var t = UseToken();
    var l = UseLevel();
  } catch (error) {
    return <Navigate to="/" state={{ from: location }} replace />;
  }
  
  const confirmToken = async () => {
    return await checkToken(t);
  };

  var v = confirmToken().then((result) => {
    return result;
  });

  return v & (l in allowedRoles) ? (
    <Outlet />
  ) : v ? (
    <Navigate to="/noAuthorized" state={{ from: location }} replace />
  ) : (
    <Navigate to="/" state={{ from: location }} replace />
  );
};

Obs: I am probably doing something really wrong here.

I need it to return a boolean so that the ternary works in the return().

  • [That `.then((result) => { return result; })` is pointless](https://stackoverflow.com/q/41089122/1048572) – Bergi Nov 01 '22 at 18:35

2 Answers2

0

Any async function will always return a Promise, change your code to:

const [token, setToken] = useState<string|undefined>()

useEffect(() => {
    confirmToken().then((result) => {
        setToken(result);
    });
}, []);

if(token) {
    if(l in allowedRoles) {
         return (<Outlet />);
    } else {
        return (<Navigate to="/noAuthorized" state={{ from: location }} replace />);
    }
}

return (<Navigate to="/" state={{ from: location }} replace />);
gunwin
  • 4,578
  • 5
  • 37
  • 59
  • 3
    Why not just `const result = await confirmToken()`? – Andy Nov 01 '22 at 15:59
  • @Andy because it conflicts with another part of the code. I wish there was another solution but meh. – Renato C.Francisco Nov 01 '22 at 16:03
  • Code updated, to use useEffect to fetch the token, and update a state var on promise resolution. – gunwin Nov 01 '22 at 16:07
  • 1
    I didn't spot what the `confirmToken` function was doing. You could even do `const result = checkToken(t)` to save you using that intermediate function. – Andy Nov 01 '22 at 16:09
0

If you want to perform asynchronous logic, you will need to use async/await like so.

Note: I created a sleep function that waits for a set duration to mimic a network call.

const ADMIN_TOKEN = '000-000';

const sleep = async (ms) =>
  new Promise((resolve) => setTimeout(resolve, ms));

const check = async (token) =>
  sleep(2000).then(() => token === ADMIN_TOKEN);

const config = { token: '000-000' };

const confirmToken = async () => check(config.token);

console.log('Waiting 2 seconds for the check to pass...');

(async () => {
  const result = await confirmToken();
  
  console.log(result);
})();

The check function can also be written as:

const check = async (token) => {
  await sleep(2000);
  return token === ADMIN_TOKEN;
};  

React example

If you want to do this in React, you will need to combine an asynchronous IIFE with a useEffect:

See: https://codesandbox.io/s/react-async-useeffect-sdjjyo?file=/src/App.jsx

import "./styles.css";
import { useEffect, useState } from "react";

const ADMIN_TOKEN = "000-000";

const sleep = async (ms) => new Promise((resolve) => setTimeout(resolve, ms));

const check = async (token) => sleep(2000).then(() => token === ADMIN_TOKEN);

const config = { token: "000-000" };

const confirmToken = async () => check(config.token);

const App = () => {
  const [authorized, setAuthorized] = useState(null);

  // Asynchronous effect
  useEffect(() => {
    (async () => {
      const confirmed = await confirmToken();
      setAuthorized(confirmed);
    })();
  }, []);

  return (
    <div className="App">
      <span data-authorized={authorized ?? ""}>
        {authorized == null
          ? "Confirming..."
          : authorized
            ? "Authorized"
            : "Unauthorized"}
      </span>
    </div>
  );
};

export default App;
Mr. Polywhirl
  • 42,981
  • 12
  • 84
  • 132