@nonreactive directive not working as expected

This is for web.

I’m trying to implement @nonreactive in my codebase but I really can’t get it to work.

The only problem that I think might be happening is that the fragment that needs to be nonreactive is deeply nested Apollo can’t target it. Also the updates to the data item happen via subscriptions but I think that’s expected?

I’ve added @nonreactive from the query all the way to the fragment and that still hasn’t worked. I’ve also registered all of the fragments via createFragmentRegistry and nothing.

There’s not a lot of info out there about @nonreactive so just wondering if there’s something that needs to be done to get it working properly that isn’t well documented.

Some ideas is if the subscription also needs to have @nonreactive, that this only works well on shallow queries, or does it not play well with variables on the query (which the top level query has)? Is there maybe something that has to be done on the server?

Thanks.

Hi @rdelga80!

Could you maybe show a few code examples what you are doing right now, and what behaviour you are expecting?

Sure. I’m going to just replace specifics but hopefully it still gets the point across.

These are my queries and fragments, I’m going to write them as I would expect would be needed to use @nonreactive:

const QUERY = gql`
  ${PAGE_CHILDREN_FRAGMENT}
  query MyQuery(
    $url: String!
    $filters: Filter # objects of arrays of strings
    $filterInput: FilterInput
    $selectedFilterId: ID
    $offset: Int!
    $limit: Int!
  ) {
    page(canonicalUrl: $url) {
      id
      pageChildren {
        id
        ...PageChildrenFragment @nonreactive
      }
    }
  }
`

export const PAGE_CHILDREN_FRAGMENT = gql`
  ${FILTER}
  ${SHELF_CHILDREN_FRAGMENT}
  fragment PageChildrenFragment on PageChildren {
    id
    filters(filterInput: $filterInput) {
      ...Filter
    }
    shelfChildren (
      filters: $filterInput
      limit: $limit
      offset: $offset
    ) {
      ...ShelfChildrenFragment @nonreactive
    }
  }
`

const SHELF_CHILDREN_FRAGMENT = gql`
  ${ITEM_FRAGMENT}
  fragment ShelfChildrenFragment on ShelfChildren {
    nextOffset
    shelfSectionChildren {
      id
      ...ItemFragment @nonreactive
    }
  }
`

const { data } = useQuery(QUERY, variables: { ... })

I have a subscription that’s updating the Item but that’s all.

Whenever there’s an update to that Item, but nothing else, the data query is still returning data on update. To my understanding the QUERY should not be reacting to the update, just the Item which I’m using useFragment within a component to retrieve.

Could you please show the full useQuery call with all options?

Also, do you have any defaultOptions on your ApolloClient instance?

We use a wrapper helper on our useQuerys I’ll include both:

const useQueryWrapper = <
  TData = any,
  TVariables extends OperationVariables = OperationVariables,
>(
  query: DocumentNode | TypedDocumentNode<TData, TVariables>,
  options?: QueryHookOptions<TData, TVariables>
) => {
  const client = getClient() # our createClient instance

  return useQuery<TData, TVariables>(query, {
    client,
    ...options,
  })
}

  const { client, data, error, loading, refetch, fetchMore } = useQueryWrapper<
    MyQuery,
    MyQueryVariables
  >(QUERY, {
    fetchPolicy: 'cache-and-network',
    variables: pageVariables,
  })

And no, there’s no defaultOptions in our ApolloClient instance.

One thing to note is that we don’t use ApolloProvider because we have multiple endpoints, but none of this code interacts with the other endpoint.

On a whim: Could you try a different fetchPolicy?

Maybe something like

fetchPolicy: 'cache-and-network',
nextFetchPolicy: 'cache-first',

No difference. I tried a bunch of different combinations and nothing helped.

Hmm, okay.

Then, going back a bit:

What is the behaviour you expect here and the behaviour you see? I’m not 100% sure what you mean by

Whenever there’s an update to that Item, but nothing else, the data query is still returning data on update.

Do you mean that the component is forced to rerender when it shouldn’t, or that the value of data changes?

From the back of my head (I’d had to verify), @nonreactive would only prevent your component from rerendering as a result of data change, but if it were updated due to something else, it would still return up-to-date data.

Ok yeah, let me expand.

Typically there’s a relationship between useQuery and useSubscription via the cache. When there’s an update via a subscription to an Item then MyQuery resends the updated data of the entire query, which includes the updated nested item.

Adding @nonreactive to Item would prevent the resending of data from the top level MyQuery when a subscription updates an Item, which is not what I’m seeing.

When I add @nonreactive then MyQuery continues to resend data on every update of Item.

I have to admit that I am a bit confused by the term “resend” - I’ve never head that used in the context of React, that’s why I’m asking for clarification here.

There are two different concepts you could possibly mean by that:

  • the component using useQuery rerenders actively when the @nonreactive item changes in the cache
  • if the component using useQuery renders for another reason (maybe because a parent componetn rerenders), useQuery returns up-to-date data

Which one of the two do you mean?

Yeah, I mean the first.

The useQuery is at the top level page component. So this line of code, for example:

const { data } = useQuery(QUERY)

On every update of an Item within that query, then data will return a new set of data. So I think that means the first of the bullets.

And just to make sure, the useSubscription hook is not at the top, it’s in a nested component, preferrably one without children?
Just want to make sure, because that one could also cause rerenders :slight_smile:

Oh yes, the useSubscription is at the top but it doesn’t return any data. It just updates the cache which updates the `useQuery.

So you set the ignoreResults option on useSubscription to true? Otherwise that will cause a rerender every time a subscription value arrives.

Ok this did stop the re-rendering but it’s broken other things like pagination. But this at least unblocks from that issue.

Not sure if I’d say it’s resolved but at least it’s something. Thanks for the help!