@apollo/client
Version:
A fully-featured caching GraphQL client.
367 lines (340 loc) • 10.6 kB
JavaScript
import { c as _c } from "@apollo/client/react/internal/compiler-runtime";
import { equal } from "@wry/equality";
import * as React from "react";
import { invariant } from "@apollo/client/utilities/invariant";
import { useDeepMemo } from "./internal/useDeepMemo.js";
import { useIsomorphicLayoutEffect } from "./internal/useIsomorphicLayoutEffect.js";
import { useApolloClient } from "./useApolloClient.js";
import { useSyncExternalStore } from "./useSyncExternalStore.js";
/**
* > Refer to the [Subscriptions](https://www.apollographql.com/docs/react/data/subscriptions/) section for a more in-depth overview of `useSubscription`.
*
* @example
*
* ```jsx
* const COMMENTS_SUBSCRIPTION = gql`
* subscription OnCommentAdded($repoFullName: String!) {
* commentAdded(repoFullName: $repoFullName) {
* id
* content
* }
* }
* `;
*
* function DontReadTheComments({ repoFullName }) {
* const {
* data: { commentAdded },
* loading,
* } = useSubscription(COMMENTS_SUBSCRIPTION, { variables: { repoFullName } });
* return <h4>New comment: {!loading && commentAdded.content}</h4>;
* }
* ```
*
* @remarks
*
* #### Consider using `onData` instead of `useEffect`
*
* If you want to react to incoming data, please use the `onData` option instead of `useEffect`.
* State updates you make inside a `useEffect` hook might cause additional rerenders, and `useEffect` is mostly meant for side effects of rendering, not as an event handler.
* State updates made in an event handler like `onData` might - depending on the React version - be batched and cause only a single rerender.
*
* Consider the following component:
*
* ```jsx
* export function Subscriptions() {
* const { data, error, loading } = useSubscription(query);
* const [accumulatedData, setAccumulatedData] = useState([]);
*
* useEffect(() => {
* setAccumulatedData((prev) => [...prev, data]);
* }, [data]);
*
* return (
* <>
* {loading && <p>Loading...</p>}
* {JSON.stringify(accumulatedData, undefined, 2)}
* </>
* );
* }
* ```
*
* Instead of using `useEffect` here, we can re-write this component to use the `onData` callback function accepted in `useSubscription`'s `options` object:
*
* ```jsx
* export function Subscriptions() {
* const [accumulatedData, setAccumulatedData] = useState([]);
* const { data, error, loading } = useSubscription(query, {
* onData({ data }) {
* setAccumulatedData((prev) => [...prev, data]);
* },
* });
*
* return (
* <>
* {loading && <p>Loading...</p>}
* {JSON.stringify(accumulatedData, undefined, 2)}
* </>
* );
* }
* ```
*
* > ⚠️ **Note:** The `useSubscription` option `onData` is available in Apollo Client >= 3.7. In previous versions, the equivalent option is named `onSubscriptionData`.
*
* Now, the first message will be added to the `accumulatedData` array since `onData` is called _before_ the component re-renders. React 18 automatic batching is still in effect and results in a single re-render, but with `onData` we can guarantee each message received after the component mounts is added to `accumulatedData`.
*
* @param subscription - A GraphQL subscription document parsed into an AST by `gql`.
* @param options - Options to control how the subscription is executed.
* @returns Query result object
*/
export function useSubscription(subscription, ...t0) {
const $ = _c(36);
const [t1] = t0;
let t2;
if ($[0] !== t1) {
t2 = t1 === undefined ? {} : t1;
$[0] = t1;
$[1] = t2;
} else {
t2 = $[1];
}
const options = t2;
const client = useApolloClient(options.client);
const {
skip,
fetchPolicy,
errorPolicy,
shouldResubscribe,
context,
extensions,
ignoreResults
} = options;
let t3;
let t4;
if ($[2] !== options.variables) {
t3 = () => options.variables;t4 = [options.variables];
$[2] = options.variables;
$[3] = t3;
$[4] = t4;
} else {
t3 = $[3];
t4 = $[4];
}
const variables = useDeepMemo(t3, t4);
let t5;
if ($[5] !== client || $[6] !== context || $[7] !== errorPolicy || $[8] !== extensions || $[9] !== fetchPolicy || $[10] !== subscription || $[11] !== variables) {
t5 = () => createSubscription(
client,
subscription,
variables,
fetchPolicy,
errorPolicy,
context,
extensions
);
$[5] = client;
$[6] = context;
$[7] = errorPolicy;
$[8] = extensions;
$[9] = fetchPolicy;
$[10] = subscription;
$[11] = variables;
$[12] = t5;
} else {
t5 = $[12];
}
const recreate = t5;
const [t6, setObservable] = React.useState(options.skip ? null : recreate);
let observable = t6;
const recreateRef = React.useRef(recreate);
let t7;
if ($[13] !== recreate) {
t7 = () => {
recreateRef.current = recreate;
};
$[13] = recreate;
$[14] = t7;
} else {
t7 = $[14];
}
useIsomorphicLayoutEffect(t7);
if (skip) {
if (observable) {
setObservable(observable = null);
}
} else {
if (!observable || (client !== observable.__.client || subscription !== observable.__.query || fetchPolicy !== observable.__.fetchPolicy || errorPolicy !== observable.__.errorPolicy || !equal(variables, observable.__.variables)) && (typeof shouldResubscribe === "function" ? !!shouldResubscribe(options) : shouldResubscribe) !== false) {
setObservable(observable = recreate());
}
}
const optionsRef = React.useRef(options);
let t8;
if ($[15] !== options) {
t8 = () => {
optionsRef.current = options;
};
$[15] = options;
$[16] = t8;
} else {
t8 = $[16];
}
React.useEffect(t8);
const fallbackLoading = !skip && !ignoreResults;
let t9;
if ($[17] !== fallbackLoading) {
t9 = {
loading: fallbackLoading,
error: void 0,
data: void 0
};
$[17] = fallbackLoading;
$[18] = t9;
} else {
t9 = $[18];
}
const fallbackResult = t9;
const ignoreResultsRef = React.useRef(ignoreResults);
let t10;
if ($[19] !== ignoreResults) {
t10 = () => {
ignoreResultsRef.current = ignoreResults;
};
$[19] = ignoreResults;
$[20] = t10;
} else {
t10 = $[20];
}
useIsomorphicLayoutEffect(t10);
let t11;
if ($[21] !== observable) {
t11 = update => {
if (!observable) {
return _temp;
}
let subscriptionStopped = false;
const client_0 = observable.__.client;
const subscription_0 = observable.subscribe({
next(value) {
if (subscriptionStopped) {
return;
}
const result = {
loading: false,
data: value.data,
error: value.error
};
observable.__.setResult(result);
if (!ignoreResultsRef.current) {
update();
}
if (result.error) {
optionsRef.current.onError?.(result.error);
} else {
if (optionsRef.current.onData) {
optionsRef.current.onData({
client: client_0,
data: result
});
}
}
},
complete() {
observable.__.completed = true;
if (!subscriptionStopped && optionsRef.current.onComplete) {
optionsRef.current.onComplete();
}
}
});
return () => {
subscriptionStopped = true;
setTimeout(() => subscription_0.unsubscribe());
};
};
$[21] = observable;
$[22] = t11;
} else {
t11 = $[22];
}
observable;
let t12;
if ($[23] !== fallbackResult || $[24] !== ignoreResults || $[25] !== observable || $[26] !== skip) {
t12 = () => observable && !skip && !ignoreResults ? observable.__.result : fallbackResult;
$[23] = fallbackResult;
$[24] = ignoreResults;
$[25] = observable;
$[26] = skip;
$[27] = t12;
} else {
t12 = $[27];
}
let t13;
if ($[28] !== fallbackResult) {
t13 = () => fallbackResult;
$[28] = fallbackResult;
$[29] = t13;
} else {
t13 = $[29];
}
const ret = useSyncExternalStore(t11, t12, t13);
let t14;
if ($[30] !== observable || $[31] !== setObservable) {
t14 = () => {
invariant(!optionsRef.current.skip, 33);
if (observable?.__.completed) {
setObservable(recreateRef.current());
} else {
observable?.restart();
}
};
$[30] = observable;
$[31] = setObservable;
$[32] = t14;
} else {
t14 = $[32];
}
observable;
const restart = t14;
let t15;
if ($[33] !== restart || $[34] !== ret) {
t15 = {
...ret,
restart
};
$[33] = restart;
$[34] = ret;
$[35] = t15;
} else {
t15 = $[35];
}
return t15;
}
function _temp() {}
function createSubscription(client, query, variables, fetchPolicy, errorPolicy, context, extensions) {
const options = {
query,
variables,
fetchPolicy,
errorPolicy,
context,
extensions,
};
const __ = {
...options,
client,
completed: false,
result: {
loading: true,
data: void 0,
error: void 0,
},
setResult(result) {
__.result = result;
},
};
return Object.assign(client.subscribe(options), {
/**
* A tracking object to store details about the observable and the latest result of the subscription.
*/
__,
});
}
//# sourceMappingURL=useSubscription.js.map