What specific syntax needs to be changed in the code below in order for the value returned by the currentSession()
function to correctly set the html that is rendered to the user in the web browser?
Specifically, what needs to be changed so that the console.log("other groups is: ", groups);
line returns a valid array of groups, and so that the logic in the {isGroupOne ? <p>Welcome back!</p> : <p>Please log in.</p>}
line is able to execute with isGroupOne
having been returned as True
?
USE CASE:
A React component needs to render different content for users that are in different user groups. The groups for a given user are indeed returned from a backend service.
PROBLEM:
The problem is that the React component is printing out the user’s groups as a pending promise instead of as a valid array of group names that can be transformed into a Boolean to indicate whether or not the user is in a specific group.
As a result, the wrong content is being printed to the web browser in the example broken code below. Currently, Please log in.
is being printed in the browser, even though you can see from the logs below that the promise eventually resolves to give isGroupOne
the value of true
.
MINIMAL CODE TO REPRODUCE PROBLEM:
Here is the minimal code required to reproduce the problem:
import { fetchAuthSession } from 'aws-amplify/auth';
function Index() {
let isGroupOne = false;
async function currentSession() {
try {
const { accessToken, idToken } = (await fetchAuthSession()).tokens ?? {};
const groups = accessToken["payload"]["cognito:groups"]
console.log("these groups: ", groups);
return groups;
} catch (err) {
console.log(err);
}
}
const groups = currentSession().then(groups => {
console.log("those groups is: ", groups);
//iterate groups array to see if "GroupOne" is in the array
groups.forEach(group => {
if (group === "GroupOne") {
console.log("User is in GroupOne group");
isGroupOne = true;
console.log("isGroupOne: ", isGroupOne);
}
});
});
console.log("other groups is: ", groups);
return (
<div>
{isGroupOne ? <p>Welcome back!</p> : <p>Please log in.</p>}
</div>
);
}
export default Index;
LOGS ILLUSTRATING THE PROBLEM:
The following is printed to the console when the page defined by the above minimal code is rendered in the web browser:
other groups is: Promise {<pending>}
index.js:29 other groups is: Promise {<pending>}
index.js:10 these groups: ['GroupOne']
index.js:18 those groups is: ['GroupOne']
index.js:22 User is in GroupOne group
index.js:24 isGroupOne: true
index.js:10 these groups: ['GroupOne']
index.js:18 those groups is: ['GroupOne']
index.js:22 User is in GroupOne group
index.js:24 isGroupOne: true
2
Answers
You can use React Hooks to achieve what you want – more specifically, useState and useEffect.
useState
helps you to store state that your UI components rely on – once the state is updated, these components will get updated as well.useEffect
can be used to execute code that should be updated when some external dependency gets updated (the second parameter to the function, an empty array in this example). By using the empty array we’re stating this should only be executed once (since there’s no dependencies that would lead to this being run again).Running code: https://codesandbox.io/s/xenodochial-fermi-4mth3h?file=/src/TheIndex.tsx
It is a classic Frontend issue: rendering data that is asynchronously fetched from Backend.
There is no magic, the only solution is to conditionally render a loading state while waiting for the Backend data, then once received, you can proceed as originally planned. Some Frontend frameworks or libraries may provide helpers to automatically manage this behavior, but it always implements the same scheme under the hood.
For example in your case, using
useAsync
hook fromreact-use
: