@apollo/client
Version:
A fully-featured caching GraphQL client.
362 lines (327 loc) • 11.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, } 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