#reactjs #ionic-framework #apollo #apollo-client
#reactjs #ionic-framework #apollo #apollo-client
Вопрос:
Я пишу приложение, которое использует клиент Apollo для выполнения запросов graphql к базе данных MongoDB Realm.
Это его абстрактная структура:
<MongoContext>
<ApolloContext>
<App/>
</ApolloContext>
</MongoContext>
Компонент верхнего уровня обрабатывает аутентификацию пользователя и предоставляет контекст. Следующий компонент запускает клиент Apollo и логику кэширования и задает контекст для всего приложения.
Ожидаемый поток данных показан на схеме на этой странице. Поведение по умолчанию для useQuery
заключается в том, что Apollo:
- Пытается извлечь из кэша;
- Пытается выполнить выборку с сервера; в случае успеха сохраняет в кеш.
Моя цель — добиться возможности работы в автономном режиме. Итак, еще раз обратившись к диаграмме, самый первый запрос должен быть разрешен кэшем, когда он содержит данные. Механизм кэширования Apollo по умолчанию находится в памяти, поэтому я использую apollo-cache-persist
для кэширования его в localStorage.
Более конкретно, это требуемые условия:
- Приложение должно реагировать при запуске.
- При первом рендеринге приложение либо отключено, либо еще не прошло проверку подлинности
- Поэтому он должен считываться из кэша, если таковой имеется
- Если кэша нет, не делайте никаких запросов к серверу (все они завершатся неудачей).
- Если пользователь находится в Сети, приложение должно получить токен аутентификации для запросов
- Токен запрашивается асинхронно
- Пока токен неизвестен, считывайте только из кэша (как указано в пункте 1.2 выше).
- Когда токен известен, используйте поток данных, описанный выше
Мои основные проблемы конкретно связаны с 1.2 и 2.2 выше. Т. е. предотвращение отправки запросов Apollo на сервер, когда мы уже знаем, что это приведет к сбою.
Я также искал глобальное решение, поэтому изменение отдельных запросов с помощью skip
или useLazyQuery
не является опцией. (И я даже не уверен, что это сработает — мне все еще нужно, чтобы запросы выполнялись в кэше.)
Код:
ApolloContext
компонент:
import * as React from 'react';
import {
ApolloClient,
InMemoryCache,
ApolloProvider,
createHttpLink,
NormalizedCacheObject,
} from '@apollo/client';
import { setContext } from '@apollo/client/link/context';
import { persistCache } from 'apollo-cache-persist';
import { PersistentStorage } from 'apollo-cache-persist/types';
const ApolloContext: React.FC = ({ children }) => {
// this hook gets me the token asynchronously
// token is '' initially but eventually resolves... or not
const { token } = useToken();
const cache = new InMemoryCache();
const [client, setClient] = React.useState(createApolloClient(token, cache))
// first initialize the client without the token, then again upon receiving it
React.useEffect(() => {
const initPersistCache = async () => {
await persistCache({
cache,
storage: capacitorStorageMethods,
debug: true,
});
};
const initApollo = async () => {
await initPersistCache();
setClient(createApolloClient(token, cache));
};
if (token) {
initApollo();
} else {
initPersistCache();
}
}, [token]);
console.log('id user', id, user);
return <ApolloProvider client={client}>{children}</ApolloProvider>;
};
function createApolloClient(
token: string,
cache: InMemoryCache
) {
const graphql_url = `https://realm.mongodb.com/api/client/v2.0/app/${realmAppId}/graphql`;
const httpLink = createHttpLink({
uri: graphql_url,
});
const authorizationHeaderLink = setContext(async (_, { headers }) => {
return {
headers: {
...headers,
Authorization: `Bearer ${token}`,
},
};
});
return new ApolloClient({
link: authorizationHeaderLink.concat(httpLink),
cache,
});
}
Что я пробовал:
После попытки многих разных действий. Я нашел кое-что, что работает, но выглядит ужасно. Хитрость заключается в том, чтобы предоставить Apollo пользовательский fetch
, который отклоняет все запросы, когда пользователь не вошел в систему:
const customFetch = (input: RequestInfo, init?: RequestInit | undefined) => {
return user.isLoggedIn
? fetch(input, init)
: Promise.reject(new Response());
};
const httpLink = createHttpLink({
uri: graphql_url,
fetch: customFetch,
});
Другой способ предотвратить исходящие запросы — просто опустить link
свойство:
return new ApolloClient({
link: user.isLoggedIn
? authorizationHeaderLink.concat(httpLink)
: undefined,
cache,
});
}
Это выглядит намного чище, но теперь проблема в том, что запросы, которые не могут быть выполнены кэшем, зависают при загрузке навсегда.(связанная проблема)
Я ищу более чистый и безопасный способ сделать это.