preloadQuery without toPromise in v4

I am currently migrating to v4 React Apollo Client

I am also preloading some data with a TanStack Router loader. This is the v3 version

  loader: ({ params }) =>
    preloadQuery(Deal_Query, {
      fetchPolicy: 'cache-and-network',
      variables: { dealId: params.dealId },
    }).toPromise(),

In v4 I get this error

Property 'toPromise' does not exist on type 'PreloadedQueryRef<Deal_QueryQuery, Exact<{ dealId: number; }>, "complete" | "streaming">'

So I take away the toPromise but now my useReadQuery is always returning the first item ever queried on page load

I hope I’ve just overlooked some migration documentation for this

Edit: my dataState from the useReadQuery is always complete

Hey @BrennenRocks :waving_hand:

Shoot, looks like we forgot to include that piece in the migration guide!

toPromise moved from the queryRef to the preloadQuery function instead:

preloadQuery.toPromise(queryRef)

We did this so that queryRef was more serializable because that .toPromise method on the queryRef itself caused problems with RSC and hydration (we always had to remove during transport since promises aren’t serializable)

Looks like we got it updated in the documentation at least. Thanks for pointing out this missing info in the migration guide! I’ll get a PR to add it.

I totally missed that in the documentation, my bad. Thanks for pointing that out to me!

I’m still having the issue where the useReadQuery is returning the first item that is loaded instead of whatever item I click on.

Here’s an example of what I’m talking about

  loader: ({ params }) => {
    console.log({ paramsDealId: params.dealId });
    const queryRef = preloadQuery(Deal_Query, {
      fetchPolicy: 'cache-and-network',
      variables: { dealId: params.dealId },
    });

    return preloadQuery.toPromise(queryRef);
  },

function RouteComponent() {
  const dealQueryRef = Route.useLoaderData();
  console.log({ dealQueryRef });
  const { data } = useReadQuery(dealQueryRef);
  console.log({ data });

This issue didn’t start until v4

That’s odd :thinking:

Just to double check that its returning different query refs, can you add a property on the queryRef to make sure you’re getting different values returned from the loader?

loader: async ({ params }) => {
  // ...

  const q = await preloadQuery.toPromise(queryRef);
  // @ts-ignore
  q.dealId = params.dealId
  return q;
}

function RouteComponent() {
  const dealQueryRef = Route.useLoaderData();
  // @ts-ignore
  console.log(dealQueryRef.dealId);
}

If this looks correct, would you mind filing a bug in the @apollo/client repo? I’d like to investigate further. We really didn’t change anything related specifically to preloadQuery, though we did switch around the internals on the core API so perhaps we missed something.

1 Like

That did it :+1:

I’ll start up an issue here [4.0] `preloadQuery` variables don't update when they change · Issue #12885 · apollographql/apollo-client · GitHub

Wait… that did it as in… adding that extra property fixed the issue you were seeing? I was mostly looking at that as a debugging trick, but now I’m very curious why this would work :thinking:

I’ll post exactly what I’m seeing in both scenarios for more context haha but my data is updating properly when I manually set q.dealId

Ok so my updated code is now exactly this

  loader: async ({ params }) => {
    console.log({ paramsDealId: params.dealId });
    const queryRef = preloadQuery(Deal_Query, {
      fetchPolicy: 'cache-and-network',
      variables: { dealId: params.dealId },
    });

    const q = await preloadQuery.toPromise(queryRef);
    // @ts-expect-error - dealId is not typed
    q.dealId = params.dealId;
    return q;
  },


function RouteComponent() {
  const dealQueryRef = Route.useLoaderData();
  const { data } = useReadQuery(dealQueryRef);
  const { dealId } = Route.useParams();
  // @ts-expect-error - dealId is not typed
  console.log({ dealQueryRefDealId: dealQueryRef.dealId, dataDealId: data.dealV2?.id });


This gives me my expected output

However when I do things the “proper” way

  loader: async ({ params }) => {
    console.log({ paramsDealId: params.dealId });
    const queryRef = preloadQuery(Deal_Query, {
      fetchPolicy: 'cache-and-network',
      variables: { dealId: params.dealId },
    });
    return preloadQuery.toPromise(queryRef);

    // const q = await preloadQuery.toPromise(queryRef);
    // // @ts-expect-error - dealId is not typed
    // q.dealId = params.dealId;
    // return q;
  },

My data deal id doesn’t change and the query ref id is undefined

Is there a GraphQL schema out in the world somewhere that has a list of objects I can tap into? I could create a front end recreation of this in a sandbox if I knew of a live public gql server somewhere

FYI I have a PR up to add the toPromise change to our migration guide: Add section on change to `preloadQuery` by jerelmiller · Pull Request #12886 · apollographql/apollo-client · GitHub

For others coming to this thread, we are continuing the conversation in [4.0] `preloadQuery` variables don't update when they change · Issue #12885 · apollographql/apollo-client · GitHub :slightly_smiling_face: