@apollo/client
Version:
A fully-featured caching GraphQL client.
387 lines (350 loc) • 12.2 kB
JavaScript
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, variablesUnknownSymbol, } from "@apollo/client/utilities/internal";
import { skipToken } from "./constants.js";
import { useDeepMemo, 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(typeof options === "object" ? options.client : undefined))(query, options);
}
function useQuery_(query, t0) {
const $ = _c(38);
let t1;
if ($[0] !== t0) {
t1 = t0 === undefined ? {} : t0;
$[0] = t0;
$[1] = t1;
} else {
t1 = $[1];
}
const options = t1;
const client = useApolloClient(typeof options === "object" ? options.client : undefined);
let t2;
if ($[2] !== options) {
t2 = typeof options === "object" ? options : {};
$[2] = options;
$[3] = t2;
} else {
t2 = $[3];
}
const {
ssr
} = t2;
const watchQueryOptions = useOptions(query, options, client.defaultOptions.watchQuery);
let t3;
if ($[4] !== client || $[5] !== query || $[6] !== watchQueryOptions) {
t3 = function createState(previous) {
const observable = client.watchQuery(watchQueryOptions);
return {
client,
query,
observable,
resultData: {
current: observable.getCurrentResult(),
previousData: previous?.resultData.current.data,
variables: observable.variables
}
};
};
$[4] = client;
$[5] = query;
$[6] = watchQueryOptions;
$[7] = t3;
} else {
t3 = $[7];
}
const createState = t3;
let [state, setState] = React.useState(createState);
if (client !== state.client || query !== state.query) {
const t4 = setState;
let t5;
if ($[8] !== createState || $[9] !== state) {
t5 = createState(state);
$[8] = createState;
$[9] = state;
$[10] = t5;
} else {
t5 = $[10];
}
t4(state = t5);
}
const {
observable: observable_0,
resultData
} = state;
useInitialFetchPolicyIfNecessary(watchQueryOptions, observable_0);
useResubscribeIfNecessary(resultData, observable_0, watchQueryOptions);
const result = useResult(observable_0, resultData, ssr);
let t4;
let t5;
if ($[11] !== observable_0) {
t5 = observable_0.refetch.bind(observable_0);
$[11] = observable_0;
$[12] = t5;
} else {
t5 = $[12];
}
let t6;
if ($[13] !== observable_0) {
t6 = observable_0.fetchMore.bind(observable_0);
$[13] = observable_0;
$[14] = t6;
} else {
t6 = $[14];
}
let t7;
if ($[15] !== observable_0) {
t7 = observable_0.updateQuery.bind(observable_0);
$[15] = observable_0;
$[16] = t7;
} else {
t7 = $[16];
}
let t8;
if ($[17] !== observable_0) {
t8 = observable_0.startPolling.bind(observable_0);
$[17] = observable_0;
$[18] = t8;
} else {
t8 = $[18];
}
let t9;
if ($[19] !== observable_0) {
t9 = observable_0.stopPolling.bind(observable_0);
$[19] = observable_0;
$[20] = t9;
} else {
t9 = $[20];
}
let t10;
if ($[21] !== observable_0) {
t10 = observable_0.subscribeToMore.bind(observable_0);
$[21] = observable_0;
$[22] = t10;
} else {
t10 = $[22];
}
let t11;
if ($[23] !== t10 || $[24] !== t5 || $[25] !== t6 || $[26] !== t7 || $[27] !== t8 || $[28] !== t9) {
t11 = {
refetch: t5,
fetchMore: t6,
updateQuery: t7,
startPolling: t8,
stopPolling: t9,
subscribeToMore: t10
};
$[23] = t10;
$[24] = t5;
$[25] = t6;
$[26] = t7;
$[27] = t8;
$[28] = t9;
$[29] = t11;
} else {
t11 = $[29];
}
t4 = t11;
const obsQueryFields = t4;
const previousData = resultData.previousData;
let t12;
let rest;
if ($[30] !== result) {
const {
partial,
...t13
} = result;
rest = t13;
$[30] = result;
$[31] = rest;
} else {
rest = $[31];
}
let t13;
if ($[32] !== client || $[33] !== obsQueryFields || $[34] !== observable_0 || $[35] !== previousData || $[36] !== rest) {
t13 = {
...rest,
client,
observable: observable_0,
variables: observable_0.variables,
previousData,
...obsQueryFields
};
$[32] = client;
$[33] = obsQueryFields;
$[34] = observable_0;
$[35] = previousData;
$[36] = rest;
$[37] = t13;
} else {
t13 = $[37];
}
t12 = t13;
return t12;
}
const fromSkipToken = Symbol();
function useOptions(query, options, defaultOptions) {
const $ = _c(5);
let t0;
let t1;
if ($[0] !== defaultOptions || $[1] !== options || $[2] !== query) {
t0 = () => {
if (options === skipToken) {
const opts = {
...mergeOptions(defaultOptions, {
query,
fetchPolicy: "standby"
}),
[variablesUnknownSymbol]: true
};
opts[fromSkipToken] = true;
return opts;
}
const watchQueryOptions = mergeOptions(defaultOptions, {
...options,
query
});
if (options.skip) {
watchQueryOptions.initialFetchPolicy = options.initialFetchPolicy || options.fetchPolicy;
watchQueryOptions.fetchPolicy = "standby";
}
return watchQueryOptions;
};t1 = [query, options, defaultOptions];
$[0] = defaultOptions;
$[1] = options;
$[2] = query;
$[3] = t0;
$[4] = t1;
} else {
t0 = $[3];
t1 = $[4];
}
return useDeepMemo(t0, t1);
}
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)) {
// If skipToken was used to generate options, we won't know the correct
// initialFetchPolicy until the hook is rerendered with real options, so we
// set it the next time we get real options
if (observable[lastWatchOptions][fromSkipToken] &&
!watchQueryOptions.initialFetchPolicy) {
watchQueryOptions.initialFetchPolicy =
watchQueryOptions.fetchPolicy;
}
// 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