Skip to content

Commit

Permalink
fix: fast refresh issue with hooks
Browse files Browse the repository at this point in the history
  • Loading branch information
andreialecu authored and benjamn committed Jul 15, 2021
1 parent 2e6e2b1 commit 0390992
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 3 deletions.
1 change: 1 addition & 0 deletions config/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ module.exports = {
'.(ts|tsx)': 'ts-jest',
},
globals: {
__DEV__: true,
'ts-jest': {
diagnostics: true,
tsconfig: {
Expand Down
2 changes: 2 additions & 0 deletions scripts/memory/tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ const {
makeVar,
} = require("@apollo/client/core");

global.__DEV__ = true;

function itAsync(message, testFn) {
const start = Date.now();
let timeout;
Expand Down
17 changes: 15 additions & 2 deletions src/react/hooks/useSubscription.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import { useContext, useState, useRef, useEffect } from 'react';
import { useContext, useState, useRef, useEffect, useReducer } from 'react';
import { DocumentNode } from 'graphql';
import { TypedDocumentNode } from '@graphql-typed-document-node/core';

import { SubscriptionHookOptions } from '../types/types';
import { SubscriptionData } from '../data';
import { OperationVariables } from '../../core';
import { getApolloContext } from '../context';
import { useAfterFastRefresh } from './utils/useAfterFastRefresh';

export function useSubscription<TData = any, TVariables = OperationVariables>(
subscription: DocumentNode | TypedDocumentNode<TData, TVariables>,
options?: SubscriptionHookOptions<TData, TVariables>
) {
const [, forceUpdate] = useReducer(x => x + 1, 0);
const context = useContext(getApolloContext());
const updatedOptions = options
? { ...options, subscription }
Expand All @@ -37,8 +39,19 @@ export function useSubscription<TData = any, TVariables = OperationVariables>(
subscriptionData.setOptions(updatedOptions, true);
subscriptionData.context = context;

// @ts-expect-error: __DEV__ is a global exposed by react
if (__DEV__) {
// ensure we run an update after refreshing so that we can resubscribe
useAfterFastRefresh(forceUpdate);
}

useEffect(() => subscriptionData.afterExecute());
useEffect(() => subscriptionData.cleanup.bind(subscriptionData), []);
useEffect(() => {
return () => {
subscriptionData.cleanup();
subscriptionDataRef.current = undefined;
};
}, []);

return subscriptionData.execute(result);
}
30 changes: 30 additions & 0 deletions src/react/hooks/utils/useAfterFastRefresh.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { useEffect, useRef } from "react";

/**
* This hook allows running a function only immediately after a react
* fast refresh or live reload.
*
* Useful in order to ensure that we can reinitialize things that have been
* disposed by cleanup functions in `useEffect`.
* @param effectFn a function to run immediately after a fast refresh
*/
export function useAfterFastRefresh(effectFn: () => unknown) {
// @ts-expect-error: __DEV__ is a global exposed by react
if (__DEV__) {
const didRefresh = useRef(false);
useEffect(() => {
return () => {
// Detect fast refresh, only runs multiple times in fast refresh
didRefresh.current = true;
};
}, []);

useEffect(() => {
if (didRefresh?.current === true) {
// This block only runs after a fast refresh
didRefresh.current = false;
effectFn();
}
}, [])
}
}
14 changes: 13 additions & 1 deletion src/react/hooks/utils/useBaseQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { QueryData } from '../../data';
import { useDeepMemo } from './useDeepMemo';
import { OperationVariables } from '../../../core';
import { getApolloContext } from '../../context';
import { useAfterFastRefresh } from './useAfterFastRefresh';

export function useBaseQuery<TData = any, TVariables = OperationVariables>(
query: DocumentNode | TypedDocumentNode<TData, TVariables>,
Expand Down Expand Up @@ -70,8 +71,19 @@ export function useBaseQuery<TData = any, TVariables = OperationVariables>(
? (result as QueryTuple<TData, TVariables>)[1]
: (result as QueryResult<TData, TVariables>);

// @ts-expect-error: __DEV__ is a global exposed by react
if (__DEV__) {
// ensure we run an update after refreshing so that we reinitialize
useAfterFastRefresh(forceUpdate);
}

useEffect(() => {
return () => queryData.cleanup();
return () => {
queryData.cleanup();
// this effect can run multiple times during a fast-refresh
// so make sure we clean up the ref
queryDataRef.current = undefined;
}
}, []);

useEffect(() => queryData.afterExecute({ lazy }), [
Expand Down

0 comments on commit 0390992

Please sign in to comment.