UNPKG

@apollo/client

Version:

A fully-featured caching GraphQL client.

362 lines (327 loc) 11.2 kB
import { c as _c } from "@apollo/client/react/internal/compiler-runtime"; /** * Function parameters in this file try to follow a common order for the sake of * readability and consistency. The order is as follows: * * resultData * observable * client * query * options * watchQueryOptions * makeWatchQueryOptions */ /** */ import { equal } from "@wry/equality"; import * as React from "react"; import { asapScheduler, observeOn } from "rxjs"; import { NetworkStatus } from "@apollo/client"; import { maybeDeepFreeze, mergeOptions, } from "@apollo/client/utilities/internal"; import { wrapHook } from "./internal/index.js"; import { useApolloClient } from "./useApolloClient.js"; import { useSyncExternalStore } from "./useSyncExternalStore.js"; const lastWatchOptions = Symbol(); export function useQuery(query, ...[options]) { "use no memo"; return wrapHook("useQuery", // eslint-disable-next-line react-compiler/react-compiler useQuery_, useApolloClient(options && options.client))(query, options); } function useQuery_(query, t0) { const $ = _c(46); let t1; if ($[0] !== t0) { t1 = t0 === undefined ? {} : t0; $[0] = t0; $[1] = t1; } else { t1 = $[1]; } const options = t1; const client = useApolloClient(options.client); let opts; let skip; if ($[2] !== options) { const { skip: t2, ssr, ...t3 } = options; skip = t2; opts = t3; $[2] = options; $[3] = opts; $[4] = skip; } else { opts = $[3]; skip = $[4]; } let watchQueryOptions; if ($[5] !== client.defaultOptions.watchQuery || $[6] !== options.fetchPolicy || $[7] !== options.initialFetchPolicy || $[8] !== opts || $[9] !== query || $[10] !== skip) { watchQueryOptions = mergeOptions(client.defaultOptions.watchQuery, { ...opts, query }); if (skip) { watchQueryOptions.initialFetchPolicy = options.initialFetchPolicy || options.fetchPolicy; watchQueryOptions.fetchPolicy = "standby"; } $[5] = client.defaultOptions.watchQuery; $[6] = options.fetchPolicy; $[7] = options.initialFetchPolicy; $[8] = opts; $[9] = query; $[10] = skip; $[11] = watchQueryOptions; } else { watchQueryOptions = $[11]; } let t2; if ($[12] !== client || $[13] !== query || $[14] !== watchQueryOptions) { t2 = function createState(previous) { const observable = client.watchQuery(watchQueryOptions); return { client, query, observable, resultData: { current: observable.getCurrentResult(), previousData: previous?.resultData.current.data, variables: observable.variables } }; }; $[12] = client; $[13] = query; $[14] = watchQueryOptions; $[15] = t2; } else { t2 = $[15]; } const createState = t2; let [state, setState] = React.useState(createState); if (client !== state.client || query !== state.query) { const t3 = setState; let t4; if ($[16] !== createState || $[17] !== state) { t4 = createState(state); $[16] = createState; $[17] = state; $[18] = t4; } else { t4 = $[18]; } t3(state = t4); } const { observable: observable_0, resultData } = state; useInitialFetchPolicyIfNecessary(watchQueryOptions, observable_0); useResubscribeIfNecessary(resultData, observable_0, watchQueryOptions); const result = useResult(observable_0, resultData, options.ssr); let t3; let t4; if ($[19] !== observable_0) { t4 = observable_0.refetch.bind(observable_0); $[19] = observable_0; $[20] = t4; } else { t4 = $[20]; } let t5; if ($[21] !== observable_0) { t5 = observable_0.fetchMore.bind(observable_0); $[21] = observable_0; $[22] = t5; } else { t5 = $[22]; } let t6; if ($[23] !== observable_0) { t6 = observable_0.updateQuery.bind(observable_0); $[23] = observable_0; $[24] = t6; } else { t6 = $[24]; } let t7; if ($[25] !== observable_0) { t7 = observable_0.startPolling.bind(observable_0); $[25] = observable_0; $[26] = t7; } else { t7 = $[26]; } let t8; if ($[27] !== observable_0) { t8 = observable_0.stopPolling.bind(observable_0); $[27] = observable_0; $[28] = t8; } else { t8 = $[28]; } let t9; if ($[29] !== observable_0) { t9 = observable_0.subscribeToMore.bind(observable_0); $[29] = observable_0; $[30] = t9; } else { t9 = $[30]; } let t10; if ($[31] !== t4 || $[32] !== t5 || $[33] !== t6 || $[34] !== t7 || $[35] !== t8 || $[36] !== t9) { t10 = { refetch: t4, fetchMore: t5, updateQuery: t6, startPolling: t7, stopPolling: t8, subscribeToMore: t9 }; $[31] = t4; $[32] = t5; $[33] = t6; $[34] = t7; $[35] = t8; $[36] = t9; $[37] = t10; } else { t10 = $[37]; } t3 = t10; const obsQueryFields = t3; const previousData = resultData.previousData; let t11; let rest; if ($[38] !== result) { const { partial, ...t12 } = result; rest = t12; $[38] = result; $[39] = rest; } else { rest = $[39]; } let t12; if ($[40] !== client || $[41] !== obsQueryFields || $[42] !== observable_0 || $[43] !== previousData || $[44] !== rest) { t12 = { ...rest, client, observable: observable_0, variables: observable_0.variables, previousData, ...obsQueryFields }; $[40] = client; $[41] = obsQueryFields; $[42] = observable_0; $[43] = previousData; $[44] = rest; $[45] = t12; } else { t12 = $[45]; } t11 = t12; return t11; } function useInitialFetchPolicyIfNecessary(watchQueryOptions, observable) { "use no memo"; if (!watchQueryOptions.fetchPolicy) { watchQueryOptions.fetchPolicy = observable.options.initialFetchPolicy; } } function useResult(observable, resultData, ssr) { "use no memo"; return useSyncExternalStore(React.useCallback((handleStoreChange) => { const subscription = observable // We use the asapScheduler here to prevent issues with trying to // update in the middle of a render. `reobserve` is kicked off in the // middle of a render and because RxJS emits values synchronously, // its possible for this `handleStoreChange` to be called in that same // render. This allows the render to complete before trying to emit a // new value. .pipe(observeOn(asapScheduler)) .subscribe((result) => { const previous = resultData.current; if ( // Avoid rerendering if the result is the same equal(previous, result) && // Force rerender if the value was emitted because variables // changed, such as when calling `refetch(newVars)` which returns // the same data when `notifyOnNetworkStatusChange` is `false`. equal(resultData.variables, observable.variables)) { return; } // eslint-disable-next-line react-compiler/react-compiler resultData.variables = observable.variables; if (previous.data && !equal(previous.data, result.data)) { resultData.previousData = previous.data; } resultData.current = result; handleStoreChange(); }); // Do the "unsubscribe" with a short delay. // This way, an existing subscription can be reused without an additional // request if "unsubscribe" and "resubscribe" to the same ObservableQuery // happen in very fast succession. return () => { setTimeout(() => subscription.unsubscribe()); }; }, [observable, resultData]), () => resultData.current, () => (ssr === false ? useQuery.ssrDisabledResult : resultData.current)); } // this hook is not compatible with any rules of React, and there's no good way to rewrite it. // it should stay a separate hook that will not be optimized by the compiler function useResubscribeIfNecessary( /** this hook will mutate properties on `resultData` */ resultData, /** this hook will mutate properties on `observable` */ observable, watchQueryOptions) { "use no memo"; if (observable[lastWatchOptions] && !equal(observable[lastWatchOptions], watchQueryOptions)) { // Though it might be tempting to postpone this reobserve call to the // useEffect block, we need getCurrentResult to return an appropriate // loading:true result synchronously (later within the same call to // useQuery). Since we already have this.observable here (not true for // the very first call to useQuery), we are not initiating any new // subscriptions, though it does feel less than ideal that reobserve // (potentially) kicks off a network request (for example, when the // variables have changed), which is technically a side-effect. if (shouldReobserve(observable[lastWatchOptions], watchQueryOptions)) { observable.reobserve(watchQueryOptions); } else { observable.applyOptions(watchQueryOptions); } // Make sure getCurrentResult returns a fresh ApolloQueryResult<TData>, // but save the current data as this.previousData, just like setResult // usually does. const result = observable.getCurrentResult(); if (!equal(result.data, resultData.current.data)) { resultData.previousData = (resultData.current.data || resultData.previousData); } resultData.current = result; resultData.variables = observable.variables; } observable[lastWatchOptions] = watchQueryOptions; } function shouldReobserve(previousOptions, options) { return (previousOptions.query !== options.query || !equal(previousOptions.variables, options.variables) || (previousOptions.fetchPolicy !== options.fetchPolicy && (options.fetchPolicy === "standby" || previousOptions.fetchPolicy === "standby"))); } useQuery.ssrDisabledResult = maybeDeepFreeze({ loading: true, data: void 0, dataState: "empty", error: void 0, networkStatus: NetworkStatus.loading, partial: true, }); //# sourceMappingURL=useQuery.js.map