···11-/* eslint-disable @typescript-eslint/no-use-before-define */
22-/* eslint-disable react-hooks/exhaustive-deps */
33-44-import {
55- useReducer,
66- useRef,
77- useEffect,
88- useLayoutEffect,
99- Dispatch,
1010-} from 'react';
1111-1212-import { Subject, Operator, makeSubject, subscribe, pipe } from 'wonka';
1313-1414-import {
1515- CallbackNode,
1616- unstable_scheduleCallback as scheduleCallback,
1717- unstable_cancelCallback as cancelCallback,
1818- unstable_getCurrentPriorityLevel as getPriorityLevel,
1919-} from 'scheduler';
2020-2121-type TeardownFn = () => void;
2222-2323-interface State<R, T = R> {
2424- subject: Subject<T>;
2525- onValue: Dispatch<R>;
2626- teardown: null | TeardownFn;
2727- task: null | CallbackNode;
2828- value: R;
2929-}
3030-3131-const isServerSide = typeof window === 'undefined';
3232-const useIsomorphicEffect = !isServerSide ? useLayoutEffect : useEffect;
3333-3434-/**
3535- * Creates a stream of `input` as it's changing and pipes this stream
3636- * into the operator which creates what becomes the output of this hook.
3737- *
3838- * This hooks supports creating a synchronous, stateful value that is
3939- * updated immediately on mount and is then updated using normal effects.
4040- * It has been built to be safe for normal-mode, concurrent-mode,
4141- * strict-mode and suspense.
4242- */
4343-export const useOperator = <T, R>(
4444- operator: Operator<T, R>,
4545- input: T,
4646- init?: R
4747-): [R, Dispatch<T>] => {
4848- const subscription = useRef<State<R, T>>({
4949- subject: makeSubject<T>(),
5050- value: init as R,
5151- onValue: (value: R) => {
5252- // Before the effect triggers we update the initial value synchronously
5353- subscription.current.value = value;
5454- },
5555- teardown: null,
5656- task: null,
5757- });
5858-5959- // This is called from effects to update the current output value
6060- const [, setValue] = useReducer((x: number, value: R) => {
6161- subscription.current.value = value;
6262- return x + 1;
6363- }, 0);
6464-6565- // On mount, subscribe to the operator using the subject and schedule a teardown using scheduler (1)
6666- if (subscription.current.teardown === null) {
6767- observe(
6868- operator,
6969- subscription.current,
7070- /* shouldScheduleTeardown */ !isServerSide
7171- );
7272- // Send the initial input value to the operator; this may call `onValue` synchronously
7373- subscription.current.subject.next(input);
7474- if (isServerSide && subscription.current.teardown !== null) {
7575- (subscription.current.teardown as any)();
7676- }
7777- }
7878-7979- // We utilise useLayoutEffect to cancel the scheduled teardown again
8080- // This works because A) useLayoutEffect runs synchronously after mount
8181- // during the commit phase, and B) if it runs we know that useEffect
8282- // is also going to run.
8383- useIsomorphicEffect(() => {
8484- // Cancel the scheduled teardown
8585- if (subscription.current.task !== null) {
8686- cancelCallback(subscription.current.task);
8787- }
8888-8989- // On unmount we call the teardown manually to stop the subscription
9090- return () => {
9191- if (subscription.current.teardown !== null) {
9292- subscription.current.teardown();
9393- }
9494- };
9595- }, []);
9696-9797- useEffect(() => {
9898- const isInitial = subscription.current.onValue !== setValue;
9999- // Once the effect runs, we update onValue to update this component properly
100100- // instead of mutating
101101- subscription.current.onValue = setValue;
102102-103103- // If the subscription got cancelled, which may happen during long suspense phases (?),
104104- // we restart it here without scheduling a teardown
105105- if (subscription.current.teardown === null) {
106106- observe(
107107- operator,
108108- subscription.current,
109109- /* shouldScheduleTeardown */ false
110110- );
111111- }
112112-113113- // If the input value has changed (except during the initial mount) we send it to the operator
114114- // This may call `setValue` which schedules an update
115115- if (!isInitial) {
116116- subscription.current.subject.next(input);
117117- }
118118- }, [input]);
119119-120120- return [subscription.current.value, subscription.current.subject.next];
121121-};
122122-123123-const observe = <R, T>(
124124- operator: Operator<T, R>,
125125- subscription: State<R, T>,
126126- shouldScheduleTeardown: boolean
127127-) => {
128128- // Start the subscription using the subject and operator
129129- const { unsubscribe } = pipe(
130130- operator(subscription.subject.source),
131131- subscribe((value: R) => subscription.onValue(value))
132132- );
133133-134134- // Update the current teardown to now be the subscription's unsubcribe function
135135- subscription.teardown = unsubscribe as TeardownFn;
136136-137137- // See (1): We schedule a teardown on mount that is cancelled by useLayoutEffect,
138138- // unless we're not expecting effects to run at all and the component not to be
139139- // rendered, which means this callback won't be cancelled and will unsubscribe.
140140- if (shouldScheduleTeardown) {
141141- subscription.task = scheduleCallback(
142142- getPriorityLevel(),
143143- subscription.teardown
144144- );
145145- }
146146-};
+1-1
src/hooks/useQuery.ts
···11import { DocumentNode } from 'graphql';
22import { useCallback, useMemo } from 'react';
33import { pipe, concat, fromValue, switchMap, map, scan } from 'wonka';
44+import { useOperator } from 'react-wonka';
4556import { useClient } from '../context';
67import { OperationContext, RequestPolicy } from '../types';
78import { CombinedError } from '../utils';
89import { useRequest } from './useRequest';
99-import { useOperator } from './useOperator';
1010import { initialState } from './constants';
11111212export interface UseQueryArgs<V> {
+1-1
src/hooks/useSubscription.ts
···11import { DocumentNode } from 'graphql';
22import { useCallback, useRef, useMemo } from 'react';
33import { pipe, concat, fromValue, switchMap, map, scan } from 'wonka';
44+import { useOperator } from 'react-wonka';
4556import { useClient } from '../context';
67import { CombinedError } from '../utils';
78import { OperationContext } from '../types';
89import { useRequest } from './useRequest';
99-import { useOperator } from './useOperator';
1010import { initialState } from './constants';
11111212export interface UseSubscriptionArgs<V> {