Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

react-apollo useQuery: add support for Suspense #162

Closed
hrougier opened this issue Aug 8, 2019 · 41 comments
Closed

react-apollo useQuery: add support for Suspense #162

hrougier opened this issue Aug 8, 2019 · 41 comments
Labels
project-apollo-client (legacy) LEGACY TAG DO NOT USE

Comments

@hrougier
Copy link

hrougier commented Aug 8, 2019

Sorry if this has already been discussed but I couldn't find any feature request for supporting React Suspense api.

Although Suspense isn't officially ready for data fetching, some libraries already take advantage of this feature (react-hooks-fetch, react-apollo-hooks, react-i18next, built-in React.lazy...).

Why

Data loading is quite painful to handle at the time, especially when you have multiple queries.

const { data: data1, loading: loading1 } = useQuery(query1);
const { data: data2, loading: loading2 } = useQuery(query2);
if (loading 1 || loading2 || ...) {
  // here goes some repetitive code
}

Code handling data loading is also very repetitive.

Solution

Similar to what react-apollo-hooks did, what about adding a boolean suspend experimental option (which defaults to false) that would make useQuery throw the query promise to trigger the Suspense api?

const { data } = useQuery(query1, { suspend: true });
// if we reach this line, data is populated and safe to use

This would certainly comes with some caveats like the fact it would only work with a cache-first fetch policy, but most of the time benefits of triggering Suspense are greater than caveats:

  • code becomes cleaner and simpler by allowing to write it as if everything is synchronous
  • repetitive code to handle loading can be reduced to the usage of a <Suspense> component

Question is: what is the roadmap about Suspense support ? Would it be possible to have an experimental implementation in a near future? or wait for the Suspense api to be officially ready for data fetching?

Thanks

@hrougier hrougier changed the title useQuery: add support for Suspense react-apollo useQuery: add support for Suspense Aug 8, 2019
@zicodeng
Copy link

I think this is a very important feature to have. I wonder why it hasn't attracted that much attention yet... I also came from react-apollo-hooks and relies on its suspense feature a lot :)

@jfrolich
Copy link

Agree. Actually I think it's pretty easy to implement it yourself by wrapping useQuery. But it would be really nice to have it as an experimental (opt-in) option to the hook!

@zicodeng
Copy link

@jfrolich do you mind sharing your implementation on useQuery wrapper that supports suspense?

@jfrolich
Copy link

jfrolich commented Oct 14, 2019

I don't use suspense for useQuery yet. But it's as simple as throwing a promise when data is not in the cache and needs to be fetched. Now that I think of it, I don't think the useQuery exposes a promise so you might need to do more than just wrapping useQuery.

@YoruNoHikage
Copy link

YoruNoHikage commented Oct 14, 2019

It's a bit more of just throwing the promise, since the current useQuery rely on useRef which is dropped when something is thrown. I had to make a few changes to the original code and might have some leaks/bugs.

I removed everything that is linked to lazy queries, it was easier to work on it like this.

Taken from https://github.com/apollographql/react-apollo/blob/master/packages/hooks/src/utils/useBaseQuery.ts

import {useQuery as useApolloQuery} from '@apollo/react-hooks';
import {getApolloContext} from '@apollo/react-common';
import {QueryData} from '@apollo/react-hooks/lib/data/QueryData';
import {useDeepMemo} from '@apollo/react-hooks/lib/utils/useDeepMemo';
import {useContext, useEffect, useReducer} from 'react';

+ // we need to store all queries references somewhere
+ // since the refs are dropped when throwing the promise
+ // this lives in the module for now but could be stored in the context, I guess
+ const queryDataRefs = new Map();
const forceUpdateRefs = new Map();

export default function useBaseQuery(query, options) {
+	// Oops, conditional hook, but let's use the real useQuery if suspense is not needed
+	/* eslint-disable */
+	if (options && !options.suspend) {
+		return useApolloQuery(query, options);
+	}
+	/* eslint-enable */

	const context = useContext(getApolloContext());
-	const [tick, forceUpdate] = useReducer(x => x + 1);
+	const [tick, forceUpdate] = useState(0);
	const updatedOptions = options
		? {...options, query}
		: {query};

-	const queryDataRef = useRef();
+	// we used the serialized options to keep track of the queries
+	const queryKey = JSON.stringify(updatedOptions);
+
+	if (!queryDataRefs.has(queryKey)) {
+		queryDataRefs.set(
+			queryKey,
+			new QueryData({
+				options: updatedOptions,
+				context,
+				forceUpdate: () => {
+					const snapshotOfForceUpdate = Array.from(
+						forceUpdateRefs.values(),
+					);
+
+					snapshotOfForceUpdate.forEach((fn) => {
+						forceUpdateRefs.delete(fn);
+						fn(x => x + 1);
+					});
+				},
+			}),
+		);
+	}

-	const queryData = queryDataRef.current;
+	const queryData = queryDataRefs.get(queryKey);
	queryData.setOptions(updatedOptions);
	queryData.context = context;

+	// I had a hard time with this one
+	// if you have several parent-children components that triggers the same query,
+	// we want to keep track of all the hooks we have to forceUpdate
+	if (!forceUpdateRefs.has(forceUpdate)) {
+		forceUpdateRefs.set(forceUpdate, forceUpdate);
+	}

	// `onError` and `onCompleted` callback functions will not always have a
	// stable identity, so we'll exclude them from the memoization key to
	// prevent `afterExecute` from being triggered un-necessarily.
	const memo = {
		options: Object.assign(Object.assign({}, updatedOptions), {
			onError: undefined,
			onCompleted: undefined,
		}),
		context,
		tick,
	};

	const result = useDeepMemo(() => queryData.execute(), memo);

	const queryResult = result;

	useEffect(() => queryData.afterExecute({lazy: false}), [
		queryResult.loading,
		queryResult.networkStatus,
		queryResult.error,
		queryResult.data,
	]);

	useEffect(
		() => () => {
			queryData.cleanup();

+			// I have to admit, I don't really know for that one
+			// but better play it safe
+			queryDataRefs.delete(queryKey);
+			forceUpdateRefs.delete(forceUpdate);
		},
		[],
	);

+	if (options && options.suspend && result.loading) {
+		forceUpdateRefs.delete(forceUpdate);
+		throw result.observable.result();
+	}

	return result;
}

If anyone is willing to update this piece of code, make it easier or more robust, I'll be grateful. :)

EDIT: I updated the code, we had some trouble with it. It should work better this way.

@benseitz
Copy link

React released Concurrent Mode (Experimental) with suspense
primarily aimed at early adopters, library authors, and curious people

So since we are both curious people and library authors this might be the best time to explore how we can use suspense with apollo 🚀

https://reactjs.org/docs/concurrent-mode-suspense.html#for-library-authors

@mikesherov
Copy link

Glad to see this issue already exists and thanks for the amazing work on Apollo! I'm almost completely out of my element here, but wanted to mention: perhaps it would be interesting to infer a @defer directive whenever a Suspense Boundary is encountered?

@alflennik
Copy link

I'm on pins and needles to see how the Apollo team integrates suspense! The benefits, like useTransition support while you wait for an Apollo-powered page to load, will be massive.

@hwillson hwillson self-assigned this Nov 18, 2019
@hwillson
Copy link
Member

Apollo Client 3 will have React Suspense support. The level of this support is still a bit up in the air, as we don't want to tie the release of AC 3 to React's release schedule, and Suspense for data fetching is still experimental. At a minimum though, all Suspense functionality in the latest release channel (when AC 3 is published) will be supported.

@rajington
Copy link

Really appreciate looking at suspense for data fetching, we've been using react-apollo-hookss suspend: true in production for a couple of months and haven't noticed any issues.

Suspense and ErrorBoundarys impacts a lot of async handling code, so being able to introduce it to new Apollo users, or as part of the 3.0 upgrade process, could let people switch to hooks and the latest best practices in one step.

@alflennik
Copy link

Thank you so much for mentioning that @rajington, I had no idea there was an experimental implementation.

@danielkcz
Copy link

I wonder what are strategies to handle enforced cache-first fetchPolicy? In several cases, we needed to use network-and-cache to keep the data up to date whenever a query is re-executed (page re-visited).

The only thing that comes to my mind is to have that query for a second time there with suspense disabled and that fetchPolicy to keep cache up-to-date. It feels weird but cannot think of a better solution.

@rajington
Copy link

@alflennik just FYI react-apollo-hooks was the unoffical hooks before Apollo's, so it's nonstandard in other ways as well, like you have to wrap it with another provider

@FredyC cache-only is interesting as well, hopefully it shouldn't suspend at all since it'sn't async

@danielkcz
Copy link

@rajington No my point is how to keep data up-to-date if we cannot use other than cache-first/only fetchPolicies.

@kevinwolfcr
Copy link

What really intrigues me is how the fetch as you render approach will be accomplished. 🤯

@danielkcz
Copy link

What really intrigues me is how the fetch as you render approach will be accomplished. 🤯

I don't think that's actually a concern of the Apollo. Have a look at a really interesting discussion over at react-router

@jfrolich
Copy link

Anyone knows if there is already an experimental build of Apollo with suspense. I'd like to test it.

@maciekbb
Copy link

Apollo Client 3 will have React Suspense support. The level of this support is still a bit up in the air, as we don't want to tie the release of AC 3 to React's release schedule, and Suspense for data fetching is still experimental. At a minimum though, all Suspense functionality in the latest release channel (when AC 3 is published) will be supported.

@hwillson Does the above still hold true? I looked at v3 beta docs https://www.apollographql.com/docs/react/v3.0-beta/data/queries/ and it does not mention suspense.

@hwillson hwillson added this to Backlog in Client Team Mar 25, 2020
@hwillson
Copy link
Member

Apollo Client's suspense work has been started, but we've had to put it on-hold for a bit due to time/resource constraints. Suspense support isn't going to make it into the AC 3.0 release unfortunately, but the good news is we've just hired more AC team members, one of which is going to start by getting suspense support in place right after AC 3 launches.

Right now our focus is on paying down a lot of tech debt in Apollo Client as we nail down the new cache structure and API. A lot of this has to be done to effectively support suspense, but more immediately it has to be done to help get rid of outstanding Apollo Client bugs. We're streamlining the internals to make them much more dependable and the changes we have coming will eradicate a large amount of the open issues we currently have.

@Fasosnql
Copy link

Fasosnql commented Jun 5, 2020

Until Apollo will release official useSuspenseQuery hook I've created own hook to manage this and collaborate with Apollo Client, you can take it here: https://www.npmjs.com/package/@brainly/use-suspense-query
Just read README and enjoy!

@younes200
Copy link

Any update on when the support of Suspense will be available now that AC 3 was released ?

@hwillson
Copy link
Member

Our Suspense plans are on-hold right now due to the fact that React concurrent mode is still only available via an experimental build, and we're not sure when that's going to change. We've decided to invest our time in other areas right now, but will definitely be diving back into all of this when it looks like concurrent mode will be available in a stable release.

@brielov
Copy link

brielov commented Aug 20, 2020

@hwillson I understand that it is experimental, but it is been experimental since forever and alternatives such as urql, swr and react-query all support it if I'm not mistaken. At this point is very unlikely that the api will change drastically in the near future.

@bkniffler
Copy link

Suspense has been supported by urql for about 4 months now (https://github.com/FormidableLabs/urql/tree/main/exchanges/suspense). I really think apollo has significally changed the GraphQL client game, but I'm sad they became that library that kept on changing API and package organization without being able to adapt to new react features. I remember that it took quite a while until hooks were supported in the official apollo react core. And seeing how long it takes (@brielov has a great point about the API probably not changing) doesn't really show the innovative spirit that once drove apollo.

It doesn't really matter, I've switched to urql already, but I still hope that apollo catches up again in terms of feature parity and speed, its been my reliable und trusted gql client for years; and they still offer tremendous libraries and toolsets that help devs work with graphQL.

@hugomn
Copy link

hugomn commented Aug 20, 2020

Apollo Client 3 will have React Suspense support. The level of this support is still a bit up in the air, as we don't want to tie the release of AC 3 to React's release schedule, and Suspense for data fetching is still experimental. At a minimum though, all Suspense functionality in the latest release channel (when AC 3 is published) will be supported.

Is suspense now supported in the new Apollo Client 3?

@younes200
Copy link

Our Suspense plans are on-hold right now due to the fact that React concurrent mode is still only available via an experimental build, and we're not sure when that's going to change. We've decided to invest our time in other areas right now, but will definitely be diving back into all of this when it looks like concurrent mode will be available in a stable release.

Yes, concurrent mode is still experimental but Suspense is widely used in production with React.Lazy and ErrorBoundary paradigm : https://reactjs.org/docs/code-splitting.html#reactlazy
Could be also under experimental flag in apollo client for opt-in when needed.

@dantman
Copy link

dantman commented Aug 20, 2020

Yes, concurrent mode is still experimental but Suspense is widely used in production with React.Lazy and ErrorBoundary paradigm : https://reactjs.org/docs/code-splitting.html#reactlazy
Could be also under experimental flag in apollo client for opt-in when needed.

Yes, Suspense for Code Splitting is in production and widely used. But Suspense for Data Fetching (the kind of Suspense that Apollo would be doing) is very much part of Concurrent Mode. Data fetching suspense proper is only available in react@experimental.

@rajington
Copy link

rajington commented Aug 21, 2020

For projects starting from scratch or starting a major refactoring (e.g. adopting GraphQL), leveraging Suspense patterns significantly changes how components are written, the amount of boilerplate and 3rd-party libs, and even how other patterns like SSR are handled.

We're currently stuck on AC2 (react-apollo-hooks supports Suspense) since it'll be easier to migrate components AC2 to AC3 than from older async handling to Suspense.

I have no idea the complexity of implementing this now and what that means for other things AC has in store, and it's frustrating some React target dates for Suspense launch have been missed, but I feel there's an opportunity to leverage AC early adopters (that likely came here in search of the best data fetching implementation) to help prepare AC@experimental for when Suspense does launch.

@rafbgarcia
Copy link

rafbgarcia commented Oct 10, 2020

I was trying to make it work with Suspense and came up with the solution below. It was just a quick test and I'm not sure if it can cause problems.

useSuspendableQuery.js

import { useQuery } from "@apollo/client"

import { suspend } from "lib/suspend"

export const useSuspendableQuery = (...args) => {
  const result = useQuery(...args)
  if (result.loading) {
    suspend(new Promise((resolve) => !result.loading && resolve())).read()
  }
  return result
}

suspend.js

export const suspend = (promise) => {
  let status = "pending"
  let response

  const suspender = promise.then(
    (res) => {
      status = "success"
      response = res
    },
    (err) => {
      status = "error"
      response = err
    }
  )

  const read = () => {
    switch (status) {
      case "pending":
        throw suspender
      case "error":
        throw response
      default:
        return response
    }
  }
  const result = { read }

  return result
}

Usage

const Parent = () => {
  return (
    <React.Suspense fallback="Loading...">
      <Child />
    </React.Suspense>
  )
}

const Child = () => {
  const { data } = useSuspendableQuery(MY_QUERY)
  return data.field
}

@Quramy
Copy link

Quramy commented Dec 23, 2020

@defer and @sterem directives are mentioned at here

These directives allows to fetch data incrementally. And I want to wrap components corresponding to @derefer ed fragment with <Suspense> as the following:

const fragment = gql`
  fragment ProductDetail on Product {
    # selections to be fetched lazy
  }
`

const DeferredFragmentComponent = ({ fragmentId }) => {
  // It's similar to client.readFragment but useFragment can throw promise if the fragment is annotated as deferred and the data is not delivered 
  const { fragmentData } = useFragment({ id: fragmentId, fragment }); 
  return (...) // render fragmentData
} 

const QueryComponent = () => {
  const { queryData: { product } } = useQuery(gql`
    query MyQuery {
      product(id: "p100") {
        __typename
        id
        name
        ...ProductDetail @defer
      }
    }
    ${fragment}
  `)
  return (
    <>
      <span>{product.name}</span>
      <Suspense fallback={<Loading />}>
        <DeferredFragmentComponent fragmentId={`${product.__typename}:${product.id}`} />
      </Suspense>
    </>
  )
}

Is there anything you are planning about this?

@dimaip
Copy link

dimaip commented Dec 31, 2020

I'm using Apollo with NextJS (with concurrent mode enabled obviously).

I tried @Fasosnql's @brainly/use-suspense-query and with it I get infinite re-rendering of a component that uses it.

If I try @rafbgarcia's useSuspendableQuery example above, it works with client-only rendering, but it gets stuck on the loading state when rehydrating after SSR with nextjs. Also I get Can't perform a React state update on a component that hasn't mounted yet. in that case.

It's a bit sad to see such conservative stance on suspense support from Apollo's maintainers TBH: it's supported by literally all other data fetching libraries by now...

@brielov
Copy link

brielov commented Dec 31, 2020

I've been patiently waiting for over a year for this feature to get implemented so I can standardize the way I structure my code around suspense. Sorry to be that guy, but I think I'm done waiting for "that one" feature that haven't got enough attention from maintainers to deserve some love, even though every other library has at least opt-in support. Switching to urql after years of betting on Apollo's stack.

@guillaumeLamanda
Copy link

Just wanted to link this issue apollographql/apollo-client#5870 because it seems related according to this comment from Dan Abramov. Maybe it can help to better understand what's involved in this issue

@hwillson hwillson removed this from Backlog in Client Team Feb 18, 2021
@smozely
Copy link

smozely commented May 2, 2021

I'm super interested in this too ... expanding on what @rafbgarcia posted, as that solution wouldn't handle the error case very well ... I've got this currently

import { ApolloQueryResult, useApolloClient } from '@apollo/client';
import { OperationVariables } from '@apollo/client/core';

import { QueryOptions } from '@apollo/client/core/watchQueryOptions';

import equal from 'deep-equal';

const suspend = <TData = any>(promise: Promise<ApolloQueryResult<TData>>) => {
  let status = 'pending';
  let response: ApolloQueryResult<TData>;

  const suspender = promise.then(
    (res) => {
      status = 'success';
      response = res;
    },
    (err) => {
      status = 'error';
      response = err;
    }
  );

  const read = () => {
    switch (status) {
      case 'pending':
        throw suspender;
      case 'error':
        throw response;
      default:
        return response;
    }
  };
  return { read };
};

const suspenseCache: any = {};

export const useSuspenseQuery = <TData = any, TVariables = OperationVariables>(
  key: string,
  options: QueryOptions<TVariables, TData>
): TData => {
  const client = useApolloClient();

  // To prevent creating a Promise (and showing the Suspense loader for 1 render)
  // when the data is already cached.
  const optimisticResult = client.readQuery(options);
  if (optimisticResult) {
    return optimisticResult;
  }

  if (
    !suspenseCache[key] ||
    !equal(suspenseCache[key].options.variables, options.variables)
  ) {
    const query = client.query(options);
    suspenseCache[key] = {
      suspender: suspend(query),
      options,
    };
  }

  return suspenseCache[key].suspender.read().data;
};

The main difference is that we have to return the same promise each time the hook is called, which means needing to save it in a cache, and thus needing to give each query a cache key (I feel like I learned this from the react-query source code).

Note:

  • using this, when running a mutation refetchQueries won't work if you pass a string[]. I think thats a side effect of not actually using the useQuery hook. Works ok when passing an array of query objects.

Seems to be working ok, so just sharing.

Edit:

  • Added note about refetch

@quinn
Copy link

quinn commented May 20, 2021

highly recommend checking out urql if you want to use suspense with a graphql backend. It has builtin suspense support, just init the client with suspense: true and you are good to go. Everything else works basically the same as apollo, at least so far for me. and if you are using typescript https://www.graphql-code-generator.com/docs/plugins/typescript-urql works great for generating typed hooks, you don't have to apply the types to the hooks yourself so its much more convenient.

@rajington
Copy link

React 18 will bring Suspense for Data Fetching, and they're stating that their API in the published alpha is stable enough for library authors.

Does this mean we could see it in Apollo Client soon? Just really trying not to migrate the codebase to urql just yet...

@hwillson
Copy link
Member

hwillson commented Jun 8, 2021

@rajington Yes, definitely. Now that things have stabilized, we'll be tackling this in Apollo Client 4. That work is being tracked here: #366

@hwillson
Copy link
Member

Hi all - we're tracking this work in #366. Please join in the discussion over there if you're still interested in this functionality. Thanks!

@llamadeus
Copy link

llamadeus commented Nov 7, 2021

Instead of messing around with the useQuery hook, you can simply render a component which throws a promise while loading, which gets resolve when that component gets unmounted.

Edit: Make your lives easier and use kanitsharma/react-suspender instead.

// Suspender.ts
export function Suspender(): ReactElement {
  const resolve = useRef<() => void>();
  const promise = useMemo(() => new Promise<void>((res) => {
    resolve.current = res;
  }), []);

  useEffect(() => {
    return () => {
      resolve.current?.();
    };
  });

  throw promise;
}

Then, in your component, render the Suspender while loading:

// Dashboard.tsx
export function Dashboard() {
  const { data, loading } = useQuery(ME_QUERY);

  if (loading) {
    return <Suspender/>;
  }

  return (
    <div>
      <h1>Hello {data?.me?.firstName}</h1>
    </div>
  );
}

Have a look at my gist to see the full example: https://gist.github.com/llamadeus/ff8ffe7ac156e545575dad81142f1b6f

@mscolnick
Copy link

I wanted to keep both the code-generated typings and re-use the existing code-generated hooks (for easier migration). And got a pretty simple solution:

// useSuspendedQuery.ts
import type { QueryHookOptions, QueryTuple } from '@apollo/client';
import { suspend } from 'suspend-react';

type UseLazyQuery<TData, TVariables> = (options?: QueryHookOptions<TData, TVariables>) => QueryTuple<TData, TVariables>;

export function useSuspendedQuery<Query, Variables>(
  useHook: UseLazyQuery<Query, Variables>,
  baseOptions?: QueryHookOptions<Query, Variables>
): Query {
  const [fetchData] = useHook();

  return suspend(async () => {
    const result = await fetchData(baseOptions);
    if (!result.data) {
      throw new Error('No data');
    }
    return result.data;
  }, [useHook.name, JSON.stringify(baseOptions?.variables)]);
}
// Dashboard.tsx

export function Dashboard() {
  const data = useSuspendedQuery(useMeLazyQuery);

  return (
    <div>
      <h1>Hello {data?.me?.firstName}</h1>
    </div>
  );
}

@jesusrodrz
Copy link

After a a few tries a get this useSuspendedQuery working

It is still some options mapping a refetch functions and stuff like this it works

import {
  DocumentNode,
  OperationVariables,
  QueryHookOptions,
  TypedDocumentNode,
  useApolloClient,
  ApolloQueryResult,
} from "@apollo/client";
import { useRef, useState, useSyncExternalStore } from "react";

function deepEqual(objA: any, objB: any, map = new WeakMap()): boolean {
  // P1
  if (Object.is(objA, objB)) return true;

  // P2
  if (objA instanceof Date && objB instanceof Date) {
    return objA.getTime() === objB.getTime();
  }
  if (objA instanceof RegExp && objB instanceof RegExp) {
    return objA.toString() === objB.toString();
  }

  // P3
  if (
    typeof objA !== "object" ||
    objA === null ||
    typeof objB !== "object" ||
    objB === null
  ) {
    return false;
  }

  // P4
  if (map.get(objA) === objB) return true;
  map.set(objA, objB);

  // P5
  const keysA = Reflect.ownKeys(objA);
  const keysB = Reflect.ownKeys(objB);

  if (keysA.length !== keysB.length) {
    return false;
  }

  for (let i = 0; i < keysA.length; i++) {
    if (
      !Reflect.has(objB, keysA[i]) ||
      !deepEqual(objA[keysA[i]], objB[keysA[i]], map)
    ) {
      return false;
    }
  }

  return true;
}

export function useSuspendedQuery<TData = any, TVariables = OperationVariables>(
  query: DocumentNode | TypedDocumentNode<TData, TVariables>,
  props?: QueryHookOptions<TData, TVariables>
): ApolloQueryResult<TData> {
  const client = useApolloClient();
  const snapShotCache = useRef<ApolloQueryResult<TData>>();

  const [observedQuery] = useState(() => {
    const obsQuery = client.watchQuery<TData, TVariables>({ query, ...props });
    return obsQuery;
  });

  const data = useSyncExternalStore(
    (store) => {
      const unSub = observedQuery.subscribe(() => {
        store();
      });
      return () => {
        unSub.unsubscribe();
      };
    },
    () => {
      const result = observedQuery.getCurrentResult();
      const isEqual = deepEqual(snapShotCache.current, result);

      const newValue = (
        isEqual ? snapShotCache.current : result
      ) as ApolloQueryResult<TData>;

      if (!isEqual) {
        snapShotCache.current = newValue;
      }
      console.log("isEqual", isEqual);
      console.log("hasData", Boolean(newValue.data));
      return newValue;
    }
  );

  const cache = client.readQuery<TData, TVariables>({ query, ...props });

  if (!cache) {
    const { fetchPolicy, ...newProps } = props ?? {};
    const policy = client.defaultOptions.query?.fetchPolicy;

    throw client.query({
      query,
      ...newProps,
      fetchPolicy: policy,
    });
  }
  return data;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
project-apollo-client (legacy) LEGACY TAG DO NOT USE
Projects
None yet
Development

No branches or pull requests