UNPKG

@shopify/react-graphql

Version:

Tools for creating type-safe and asynchronous GraphQL components for React

201 lines (192 loc) 7.12 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var React = require('react'); var client = require('@apollo/client'); var isEqual = require('fast-deep-equal'); var reactEffect = require('@shopify/react-effect'); var apolloClient = require('./apollo-client.js'); var graphqlDocument = require('./graphql-document.js'); function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } var isEqual__default = /*#__PURE__*/_interopDefaultLegacy(isEqual); /* eslint react-hooks/rules-of-hooks: off */ const { prototype: { hasOwnProperty } } = Object; function useQuery(queryOrAsyncQuery, ...optionsPart) { const [options = {}] = optionsPart; const { skip = false, fetchPolicy, errorPolicy, pollInterval, client: overrideClient, notifyOnNetworkStatusChange, context, ssr = true } = options; const variables = options.variables || {}; const client$1 = apolloClient["default"](overrideClient); if (typeof window === 'undefined') { if (skip) { return createDefaultResult(client$1, variables); } else if (!ssr || fetchPolicy === 'no-cache') { return createDefaultResult(client$1, variables, undefined, true); } } const query = graphqlDocument["default"](queryOrAsyncQuery); const normalizedFetchPolicy = typeof window === 'undefined' && (fetchPolicy === 'network-only' || fetchPolicy === 'cache-and-network') ? 'cache-first' : fetchPolicy; const serializedVariables = variables && JSON.stringify(variables); const watchQueryOptions = React.useMemo(() => { if (!query) { return null; } return { query, context, variables, fetchPolicy: normalizedFetchPolicy, errorPolicy, pollInterval, notifyOnNetworkStatusChange }; // eslint-disable-next-line react-hooks/exhaustive-deps }, [query, // eslint-disable-next-line react-hooks/exhaustive-deps context && JSON.stringify(context), serializedVariables, normalizedFetchPolicy, errorPolicy, pollInterval, notifyOnNetworkStatusChange]); const queryObservable = React.useMemo(() => { if (skip || !watchQueryOptions) { return; } return client$1.watchQuery(watchQueryOptions); }, [client$1, skip, watchQueryOptions]); reactEffect.useServerEffect(() => { if (queryObservable == null) { return; } const result = queryObservable.getCurrentResult(); return result.loading ? queryObservable.result() : undefined; }); const defaultResult = React.useMemo(() => createDefaultResult(client$1, variables, queryObservable), // eslint-disable-next-line react-hooks/exhaustive-deps [queryObservable, client$1, serializedVariables]); const [responseId, setResponseId] = React.useState(0); React.useEffect(() => { if (skip || !queryObservable) { return; } let subscription; let previousError; const invalidateCurrentResult = () => { setResponseId(x => x + 1); }; // from: https://github.com/apollographql/react-apollo/blob/v2.2.0/src/Query.tsx#L343 // after a error on refetch, without this fix, refetch never works again function invalidateErrorResult(error) { // @ts-expect-error access last const lastResult = queryObservable === null || queryObservable === void 0 ? void 0 : queryObservable.last; unsubscribe(); try { queryObservable === null || queryObservable === void 0 || queryObservable.resetLastResults(); subscribe(); } finally { // @ts-expect-error override last queryObservable.last = lastResult; } if (!hasOwnProperty.call(error, 'graphQLErrors')) { // The error is not a GraphQL error throw error; } if (!previousError || !isEqual__default["default"](error, previousError)) { invalidateCurrentResult(); } previousError = error; } function subscribe() { subscription = queryObservable.subscribe(status => { previousError = undefined; // apollo 3 calls this when 2 didn't prevent // the extra render if (currentResult.loading === status.loading && currentResult.networkStatus === status.networkStatus && status.partial) { return; } invalidateCurrentResult(); }, error => { invalidateErrorResult(error); }); } function unsubscribe() { if (subscription) { subscription.unsubscribe(); } subscription = undefined; } subscribe(); return unsubscribe; // eslint-disable-next-line react-hooks/exhaustive-deps }, [skip, queryObservable]); const previousData = React.useRef(undefined); const currentResult = React.useMemo(() => { // must of the logic below are lifted from // https://github.com/apollographql/react-apollo/blob/22f8ebf52b26b348d6be905d5b7fbbfea51c1541/src/Query.tsx#L410-L477 if (skip) { return defaultResult; } if (!queryObservable) { return { ...defaultResult, // while documentNode is loading loading: true }; } const result = queryObservable.getCurrentResult(); const { fetchPolicy } = queryObservable.options; const hasError = result.errors && result.errors.length > 0; let data = result.data; if (result.loading) { data = previousData.current || result && result.data ? { ...(previousData.current || {}), ...(result && result.data || {}) } : undefined; } else if (hasError) { data = (queryObservable.getLastResult() || {}).data; } else if (fetchPolicy === 'no-cache' && (!result.data || Object.keys(result.data).length === 0)) { data = previousData.current; } else { previousData.current = result.data; } return { ...defaultResult, data, error: hasError ? new client.ApolloError({ graphQLErrors: result.errors }) : result.error, networkStatus: result.networkStatus, loading: result.loading }; // eslint-disable-next-line react-hooks/exhaustive-deps }, [responseId, skip, queryObservable, defaultResult, previousData]); return currentResult; } function createDefaultResult(client, variables, queryObservable, loading = false) { return { data: undefined, error: undefined, networkStatus: undefined, loading, called: false, variables: queryObservable ? queryObservable.variables : variables, refetch: queryObservable ? queryObservable.refetch.bind(queryObservable) : noop, fetchMore: queryObservable ? queryObservable.fetchMore.bind(queryObservable) : noop, updateQuery: queryObservable ? queryObservable.updateQuery.bind(queryObservable) : noop, startPolling: queryObservable ? queryObservable.startPolling.bind(queryObservable) : noop, stopPolling: queryObservable ? queryObservable.stopPolling.bind(queryObservable) : noop, subscribeToMore: queryObservable ? queryObservable.subscribeToMore.bind(queryObservable) : noop, client }; } function noop() {} exports["default"] = useQuery;