Skip to content

Instantly share code, notes, and snippets.

@jaredpalmer
Created May 14, 2019 20:24
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jaredpalmer/c2013f05935087fad36bc191c8f26fc6 to your computer and use it in GitHub Desktop.
Save jaredpalmer/c2013f05935087fad36bc191c8f26fc6 to your computer and use it in GitHub Desktop.
Suspense-ready useQuery
import { ApolloError } from 'apollo-boost';
import { print } from 'graphql/language/printer';
import React from 'react';
const queries = new Map();
const getCacheKey = ({ query, ...opts }) =>
`${print(query)}@@${JSON.stringify(opts)}`;
const invalidateCachedQuery = options =>
queries.delete(getCacheKey(options));
const getCachedQuery = (client, options) => {
const cacheKey = getCacheKey(options);
let observableQuery = queries.get(cacheKey);
if (observableQuery == null) {
observableQuery = client.watchQuery(options);
queries.set(cacheKey, observableQuery);
}
return observableQuery;
};
const actHack = fn => {
if (fn) {
fn();
}
};
export const ApolloContext = React.createContext(null);
export const ApolloProvider = props => (
<ApolloContext.Provider value={props.client}>
{props.children}
</ApolloContext.Provider>
);
export function useQuery(query, vars) {
const client = React.useContext(ApolloContext);
const options = React.useMemo(() => ({ query, ...vars }), [
query,
vars,
]);
const observableQuery = React.useMemo(
() => getCachedQuery(client, options),
[client, options]
);
const currentResult = React.useMemo(() => {
const result = observableQuery.currentResult();
let data = result.data;
if (result.error || result.errors) {
data = {
...result.data,
...(observableQuery.getLastResult() || {}).data,
};
}
return {
data,
error:
result.errors && result.errors.length > 0
? new ApolloError({ graphQLErrors: result.errors })
: result.error,
networkStatus: undefined,
partial: result.partial,
};
}, [observableQuery]);
// eslint-disable-next-line no-unused-vars
const [responseId, setResponseId] = React.useState(0);
React.useEffect(() => {
const invalidateHack = () => {
actHack(() => {
setResponseId(x => x + 1);
});
};
const subscription = observableQuery.subscribe(
invalidateHack,
invalidateHack
);
invalidateCachedQuery(options);
return () => {
subscription.unsubscribe();
};
}, [observableQuery, options]);
if (currentResult.error != null && options.onError) {
options.onError(currentResult.error);
}
if (currentResult.partial) {
throw observableQuery.result();
}
return currentResult;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment