···11+---
22+'@urql/preact': minor
33+---
44+55+Update `@urql/preact` implementation to match `urql` React implementation. Internally these changes should align behaviour and updates slightly, but outwardly no changes should be apparent apart from how some updates are scheduled.
···11-/* eslint-disable react-hooks/exhaustive-deps */
22-33-import { useRef, useEffect, EffectCallback } from 'preact/hooks';
44-import { noop } from './useQuery';
55-66-export const useImmediateEffect = (
77- effect: EffectCallback,
88- changes: ReadonlyArray<any>
99-) => {
1010- const teardown = useRef<() => void>(noop);
1111- const isMounted = useRef<boolean>(false);
1212-1313- // On initial render we just execute the effect
1414- if (!isMounted.current) {
1515- // There's the slight possibility that we had an interrupt due to
1616- // conccurrent mode after running the effect.
1717- // This could result in memory leaks.
1818- teardown.current();
1919- teardown.current = effect() || noop;
2020- }
2121-2222- useEffect(() => {
2323- // Initially we skip executing the effect since we've already done so on
2424- // initial render, then we execute it as usual
2525- return isMounted.current
2626- ? effect()
2727- : ((isMounted.current = true), teardown.current);
2828- }, changes);
2929-};
···11import { DocumentNode } from 'graphql';
22-import { useCallback, useRef } from 'preact/hooks';
33-import { pipe, onEnd, subscribe } from 'wonka';
22+import { useCallback, useRef, useMemo } from 'preact/hooks';
33+import { pipe, concat, fromValue, switchMap, map, scan } from 'wonka';
44import { CombinedError, OperationContext, Operation } from '@urql/core';
55+56import { useClient } from '../context';
77+import { useSource, useBehaviourSubject } from './useSource';
68import { useRequest } from './useRequest';
77-import { noop, initialState } from './useQuery';
88-import { useImmediateEffect } from './useImmediateEffect';
99-import { useImmediateState } from './useImmediateState';
99+import { initialState } from './constants';
10101111export interface UseSubscriptionArgs<V> {
1212 query: DocumentNode | string;
···3131 (opts?: Partial<OperationContext>) => void
3232];
33333434-export const useSubscription = <T = any, R = T, V = object>(
3434+export function useSubscription<T = any, R = T, V = object>(
3535 args: UseSubscriptionArgs<V>,
3636 handler?: SubscriptionHandler<T, R>
3737-): UseSubscriptionResponse<R> => {
3838- const unsubscribe = useRef<(_1?: any) => void>(noop);
3939- const handlerRef = useRef(handler);
3737+): UseSubscriptionResponse<R> {
4038 const client = useClient();
41394242- const [state, setState] = useImmediateState<UseSubscriptionState<R>>({
4343- ...initialState,
4444- });
4545-4640 // Update handler on constant ref, since handler changes shouldn't
4741 // trigger a new subscription run
4242+ const handlerRef = useRef(handler);
4843 handlerRef.current = handler!;
49445045 // This creates a request which will keep a stable reference
5146 // if request.key doesn't change
5247 const request = useRequest(args.query, args.variables);
53485454- const executeSubscription = useCallback(
4949+ // Create a new subscription-source from client.executeSubscription
5050+ const makeSubscription$ = useCallback(
5551 (opts?: Partial<OperationContext>) => {
5656- unsubscribe.current();
5252+ return client.executeSubscription(request, { ...args.context, ...opts });
5353+ },
5454+ [client, request, args.context]
5555+ );
57565858- setState(s => ({ ...s, fetching: true }));
5757+ const [subscription$$, update] = useBehaviourSubject(
5858+ useMemo(() => (args.pause ? null : makeSubscription$()), [
5959+ args.pause,
6060+ makeSubscription$,
6161+ ])
6262+ );
59636060- const result = pipe(
6161- client.executeSubscription(request, {
6262- ...args.context,
6363- ...opts,
6464+ const state = useSource(
6565+ useMemo(() => {
6666+ return pipe(
6767+ subscription$$,
6868+ switchMap(subscription$ => {
6969+ if (!subscription$) return fromValue({ fetching: false });
7070+7171+ return concat([
7272+ // Initially set fetching to true
7373+ fromValue({ fetching: true, stale: false }),
7474+ pipe(
7575+ subscription$,
7676+ map(({ stale, data, error, extensions, operation }) => ({
7777+ fetching: true,
7878+ stale: !!stale,
7979+ data,
8080+ error,
8181+ extensions,
8282+ operation,
8383+ }))
8484+ ),
8585+ // When the source proactively closes, fetching is set to false
8686+ fromValue({ fetching: false, stale: false }),
8787+ ]);
6488 }),
6565- onEnd(() => setState(s => ({ ...s, fetching: false }))),
6666- subscribe(result => {
6767- setState(s => ({
6868- fetching: true,
6969- data:
7070- typeof handlerRef.current === 'function'
7171- ? handlerRef.current(s.data, result.data)
7272- : result.data,
7373- error: result.error,
7474- extensions: result.extensions,
7575- stale: !!result.stale,
7676- operation: result.operation,
7777- }));
7878- })
8989+ // The individual partial results are merged into each previous result
9090+ scan((result, partial: any) => {
9191+ const { current: handler } = handlerRef;
9292+ // If a handler has been passed, it's used to merge new data in
9393+ const data =
9494+ partial.data !== undefined
9595+ ? typeof handler === 'function'
9696+ ? handler(result.data, partial.data)
9797+ : partial.data
9898+ : result.data;
9999+ return { ...result, ...partial, data };
100100+ }, initialState)
79101 );
8080- unsubscribe.current = result.unsubscribe;
8181- },
8282- [client, request, setState, args.context]
102102+ }, [subscription$$]),
103103+ initialState
83104 );
841058585- useImmediateEffect(() => {
8686- if (args.pause) {
8787- unsubscribe.current();
8888- setState(s => ({ ...s, fetching: false }));
8989- return noop;
9090- }
9191-9292- executeSubscription();
9393- return unsubscribe.current; // eslint-disable-line
9494- }, [executeSubscription, args.pause, setState]);
106106+ // This is the imperative execute function passed to the user
107107+ const executeSubscription = useCallback(
108108+ (opts?: Partial<OperationContext>) => update(makeSubscription$(opts)),
109109+ [update, makeSubscription$]
110110+ );
9511196112 return [state, executeSubscription];
9797-};
113113+}
+1-1
packages/react-urql/src/hooks/useSubscription.ts
···4040 // Update handler on constant ref, since handler changes shouldn't
4141 // trigger a new subscription run
4242 const handlerRef = useRef(handler);
4343- handlerRef.current = handler;
4343+ handlerRef.current = handler!;
44444545 // This creates a request which will keep a stable reference
4646 // if request.key doesn't change