Какой конкретный синтаксис необходимо изменить в приведенном ниже коде, чтобы значение, возвращаемое функцией currentSession()
, правильно задавало HTML-код, отображаемый пользователю в веб-браузере?
В частности, что нужно изменить, чтобы строка console.info("other groups is: ", groups);
возвращала действительный массив групп и чтобы логика в строке {isGroupOne ? <p>Welcome back!</p> : <p>Please log in.</p>}
могла выполняться с isGroupOne
, возвращенным как True
?
СЛУЧАЙ ИСПОЛЬЗОВАНИЯ:
Компоненту React необходимо отображать разный контент для пользователей, находящихся в разных группах пользователей. Группы для данного пользователя действительно возвращаются из серверной службы.
ПРОБЛЕМА:
Проблема в том, что компонент React распечатывает группы пользователя как ожидающее обещание, а не как действительный массив имен групп, который можно преобразовать в логическое значение, чтобы указать, находится ли пользователь в определенной группе.
В результате в веб-браузере выводится неправильный контент в приведенном ниже примере неработающего кода. В настоящее время Please log in.
печатается в браузере, хотя из журналов ниже видно, что обещание в конечном итоге разрешается дать isGroupOne
значение true
.
МИНИМАЛЬНЫЙ КОД ДЛЯ ВОСПРОИЗВЕДЕНИЯ ПРОБЛЕМЫ:
Вот минимальный код, необходимый для воспроизведения проблемы:
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.info("these groups: ", groups);
return groups;
} catch (err) {
console.info(err);
}
}
const groups = currentSession().then(groups => {
console.info("those groups is: ", groups);
//iterate groups array to see if "GroupOne" is in the array
groups.forEach(group => {
if (group === "GroupOne") {
console.info("User is in GroupOne group");
isGroupOne = true;
console.info("isGroupOne: ", isGroupOne);
}
});
});
console.info("other groups is: ", groups);
return (
<div>
{isGroupOne ? <p>Welcome back!</p> : <p>Please log in.</p>}
</div>
);
}
export default Index;
ЖУРНАЛЫ, ИЛЛЮСТРИРУЮЩИЕ ПРОБЛЕМУ:
Следующее сообщение выводится на консоль, когда страница, определенная указанным выше минимальным кодом, отображается в веб-браузере:
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
@JaromandaX Создание function Index()
функции async
приводит к другим ошибкам. Я пропустил эти другие ошибки, чтобы упростить этот ФП.
как я уже сказал, я не разработчик реагирования, поэтому не знаю, какие ограничения он накладывает
Вы можете использовать React Hooks для достижения желаемого, а точнее useState и useEffect.
useState
помогает вам сохранять состояние, от которого зависят ваши компоненты пользовательского интерфейса — как только состояние будет обновлено, эти компоненты также будут обновлены.
useEffect
можно использовать для выполнения кода, который должен обновляться при обновлении какой-либо внешней зависимости (второй параметр функции, в этом примере пустой массив). Используя пустой массив, мы заявляем, что это должно выполняться только один раз (поскольку нет никаких зависимостей, которые могли бы привести к повторному запуску).
import { useEffect, useState } from "react";
// Emulates retrieving the user from Cognito
async function mockedFetchAuthSession(): Promise<{ groups: string[] }> {
// adjust, save, and reload the page to see diff results
const groups = ["GroupOne"]; // <-- replace with another group do see diff results
return new Promise((resolve) => {
setTimeout(() => resolve({ groups }), 2_000);
});
}
function groupOne(group: string): boolean {
return group === "GroupOne";
}
export default function TheIndex() {
const [isGroupOne, setIsGroupOne] = useState<boolean | null>(null);
async function currentSession() {
try {
const { groups } = await mockedFetchAuthSession();
console.info("these groups: ", groups);
setIsGroupOne(groups.some(groupOne));
} catch (err) {
console.info(err);
}
}
useEffect(() => {
currentSession();
}, []);
return (
<div>
{isGroupOne === null ? (
<p>Loading...</p>
) : isGroupOne ? (
<p>Welcome back!</p>
) : (
<p>Please log in.</p>
)}
</div>
);
}
Запускающий код: https://codesandbox.io/s/xenodochial-fermi-4mth3h?file=/src/TheIndex.tsx
Спасибо и +1, что нашли время ответить на этот вопрос. Я выбрал вариант, который требует меньше кода, чем ваш. Но спасибо за ваше время. Добро пожаловать в переполнение стека.
Без проблем. Обратите внимание: для выбранного вами ответа требуется дополнительная библиотека, и этот ответ был намеренно многословен, чтобы лучше связать его с предоставленным вами кодом. Это может быть намного короче, если это то, что вы ищете.
Это классическая проблема фронтенда: рендеринг данных, которые асинхронно извлекаются из бэкенда.
Здесь нет никакой магии, единственное решение — условно отрисовать загрузочный пользовательский интерфейс во время ожидания данных Backend, а затем, как только они будут получены, вы сможете действовать, как изначально планировалось. Некоторые фреймворки или библиотеки Frontend могут предоставлять помощники для автоматического управления этим поведением, но они всегда реализуют одну и ту же схему «под капотом».
Например, в вашем случае, используя крючок useAsync
из реакции-использования:
npm install react-use
import React, { useMemo } from "react";
import { useAsync } from "react-use";
function Index() {
const groupsAsync = useAsync(currentSession, []); // Fill the dependency array if session should be refreshed on some criteria
const isGroupOne = useMemo(() =>
!groupsAsync.loading &&
groupsAsync.value?.some(
(group) => group === "GroupOne"
), [groupsAsync.loading, groupsAsync.value]);
return (
<div>
{groupsAsync.loading
? "Loading..." // Any UI you want as loader, e.g. skeleton, or nothing at all...
: isGroupOne
? <p>Welcome back!</p>
: <p>Please log in.</p>}
</div>
);
}
Спасибо и +1 за то, что вы не только нашли время ответить на мой вопрос, но и за то, что выслушали и уточнили, когда я задал дополнительный вопрос.
я не разработчик реагирования, поэтому извините, если это невозможно... создайте
function Index()
функциюasync
, тогда вы сможете дождаться обещанияgroups