UNPKG

6.85 kBJavaScriptView Raw
1import { __assign } from "tslib";
2import * as React from "rehackt";
3import { mergeOptions } from "../../utilities/index.js";
4import { createMakeWatchQueryOptions, getDefaultFetchPolicy, getObsQueryOptions, toQueryResult, useQueryInternals, } from "./useQuery.js";
5import { useIsomorphicLayoutEffect } from "./internal/useIsomorphicLayoutEffect.js";
6// The following methods, when called will execute the query, regardless of
7// whether the useLazyQuery execute function was called before.
8var EAGER_METHODS = [
9 "refetch",
10 "reobserve",
11 "fetchMore",
12 "updateQuery",
13 "startPolling",
14 "stopPolling",
15 "subscribeToMore",
16];
17/**
18 * A hook for imperatively executing queries in an Apollo application, e.g. in response to user interaction.
19 *
20 * > Refer to the [Queries - Manual execution with useLazyQuery](https://www.apollographql.com/docs/react/data/queries#manual-execution-with-uselazyquery) section for a more in-depth overview of `useLazyQuery`.
21 *
22 * @example
23 * ```jsx
24 * import { gql, useLazyQuery } from "@apollo/client";
25 *
26 * const GET_GREETING = gql`
27 * query GetGreeting($language: String!) {
28 * greeting(language: $language) {
29 * message
30 * }
31 * }
32 * `;
33 *
34 * function Hello() {
35 * const [loadGreeting, { called, loading, data }] = useLazyQuery(
36 * GET_GREETING,
37 * { variables: { language: "english" } }
38 * );
39 * if (called && loading) return <p>Loading ...</p>
40 * if (!called) {
41 * return <button onClick={() => loadGreeting()}>Load greeting</button>
42 * }
43 * return <h1>Hello {data.greeting.message}!</h1>;
44 * }
45 * ```
46 * @since 3.0.0
47 *
48 * @param query - A GraphQL query document parsed into an AST by `gql`.
49 * @param options - Default options to control how the query is executed.
50 * @returns A tuple in the form of `[execute, result]`
51 */
52export function useLazyQuery(query, options) {
53 var _a;
54 var execOptionsRef = React.useRef();
55 var optionsRef = React.useRef();
56 var queryRef = React.useRef();
57 var merged = mergeOptions(options, execOptionsRef.current || {});
58 var document = (_a = merged === null || merged === void 0 ? void 0 : merged.query) !== null && _a !== void 0 ? _a : query;
59 // Use refs to track options and the used query to ensure the `execute`
60 // function remains referentially stable between renders.
61 optionsRef.current = options;
62 queryRef.current = document;
63 var queryHookOptions = __assign(__assign({}, merged), { skip: !execOptionsRef.current });
64 var _b = useQueryInternals(document, queryHookOptions), obsQueryFields = _b.obsQueryFields, useQueryResult = _b.result, client = _b.client, resultData = _b.resultData, observable = _b.observable, onQueryExecuted = _b.onQueryExecuted;
65 var initialFetchPolicy = observable.options.initialFetchPolicy ||
66 getDefaultFetchPolicy(queryHookOptions.defaultOptions, client.defaultOptions);
67 var forceUpdateState = React.useReducer(function (tick) { return tick + 1; }, 0)[1];
68 // We use useMemo here to make sure the eager methods have a stable identity.
69 var eagerMethods = React.useMemo(function () {
70 var eagerMethods = {};
71 var _loop_1 = function (key) {
72 var method = obsQueryFields[key];
73 eagerMethods[key] = function () {
74 if (!execOptionsRef.current) {
75 execOptionsRef.current = Object.create(null);
76 // Only the first time populating execOptionsRef.current matters here.
77 forceUpdateState();
78 }
79 // @ts-expect-error this is just too generic to type
80 return method.apply(this, arguments);
81 };
82 };
83 for (var _i = 0, EAGER_METHODS_1 = EAGER_METHODS; _i < EAGER_METHODS_1.length; _i++) {
84 var key = EAGER_METHODS_1[_i];
85 _loop_1(key);
86 }
87 return eagerMethods;
88 }, [forceUpdateState, obsQueryFields]);
89 var called = !!execOptionsRef.current;
90 var result = React.useMemo(function () { return (__assign(__assign(__assign({}, useQueryResult), eagerMethods), { called: called })); }, [useQueryResult, eagerMethods, called]);
91 var execute = React.useCallback(function (executeOptions) {
92 execOptionsRef.current =
93 executeOptions ? __assign(__assign({}, executeOptions), { fetchPolicy: executeOptions.fetchPolicy || initialFetchPolicy }) : {
94 fetchPolicy: initialFetchPolicy,
95 };
96 var options = mergeOptions(optionsRef.current, __assign({ query: queryRef.current }, execOptionsRef.current));
97 var promise = executeQuery(resultData, observable, client, document, __assign(__assign({}, options), { skip: false }), onQueryExecuted).then(function (queryResult) { return Object.assign(queryResult, eagerMethods); });
98 // Because the return value of `useLazyQuery` is usually floated, we need
99 // to catch the promise to prevent unhandled rejections.
100 promise.catch(function () { });
101 return promise;
102 }, [
103 client,
104 document,
105 eagerMethods,
106 initialFetchPolicy,
107 observable,
108 resultData,
109 onQueryExecuted,
110 ]);
111 var executeRef = React.useRef(execute);
112 useIsomorphicLayoutEffect(function () {
113 executeRef.current = execute;
114 });
115 var stableExecute = React.useCallback(function () {
116 var args = [];
117 for (var _i = 0; _i < arguments.length; _i++) {
118 args[_i] = arguments[_i];
119 }
120 return executeRef.current.apply(executeRef, args);
121 }, []);
122 return [stableExecute, result];
123}
124function executeQuery(resultData, observable, client, currentQuery, options, onQueryExecuted) {
125 var query = options.query || currentQuery;
126 var watchQueryOptions = createMakeWatchQueryOptions(client, query, options, false)(observable);
127 var concast = observable.reobserveAsConcast(getObsQueryOptions(observable, client, options, watchQueryOptions));
128 onQueryExecuted(watchQueryOptions);
129 return new Promise(function (resolve) {
130 var result;
131 // Subscribe to the concast independently of the ObservableQuery in case
132 // the component gets unmounted before the promise resolves. This prevents
133 // the concast from terminating early and resolving with `undefined` when
134 // there are no more subscribers for the concast.
135 concast.subscribe({
136 next: function (value) {
137 result = value;
138 },
139 error: function () {
140 resolve(toQueryResult(observable.getCurrentResult(), resultData.previousData, observable, client));
141 },
142 complete: function () {
143 resolve(toQueryResult(result, resultData.previousData, observable, client));
144 },
145 });
146 });
147}
148//# sourceMappingURL=useLazyQuery.js.map
\No newline at end of file