React-apollo: refetch() does not update loading to true

Created on 11 Nov 2016  Â·  62Comments  Â·  Source: apollographql/react-apollo

Steps to Reproduce

import React from 'react';
import { graphql } from 'react-apollo';
import gql from 'graphql-tag';

const App = ({ data }) => {
  console.log('loading', data.loading);
  return (
    <button
      onClick={() => {
        console.log('refetch');
        data.refetch();
      }}
    >refetch</button>
  );
};

const query = gql`
  query allTypes {
      __schema {
          types {
              name
          }
      }
  }
`;

export default graphql(query)(App);

Buggy Behavior

When I call props.data.refetch(), props.data.loading is still false.
image

Expected Behavior

I thought it would change loading to true.

From docs:

loading: This field is true if there is currently a query fetch in flight, including after calling refetch. false otherwise.

Version

Most helpful comment

Aha, I might have found a workaround / solution: there is a notifyOnNetworkStatusChange option on watchQuery, which is only documented in the API reference.

With that option set, I get this output:

loading true networkStatus 1
loading false networkStatus 7
refetch
loading false networkStatus 2
loading false networkStatus 7
refetch
loading false networkStatus 4
loading false networkStatus 7
refetch
loading false networkStatus 4
loading false networkStatus 7

This is workable, but definitely surprising behavior that definitely needs to be better documented and possibly revisited.

Some specific feedback of things that are surprising to me:

  • My original intuition of the loading value is that it would be true whenever a query was in flight.
    Instead, it seems to be true only on the initial load? (so it acts more like what I would call !loaded) Although I think I've seen it also be true when the query's variables were updated...?
  • As mentioned above, networkStatus only receives updates if you pass the notifyOnNetworkStatusChange flag.
  • networkStatus is a number which doesn't mean much of anything to me. The only place this is "documented" is, as far as I'm aware, the type definition in the source code.
  • The first refetch's networkStatus, according to that type definition, is setVariables, rather than refetch, which is what I would expect and what it is on subsequent refetches. Is this a bug?

All 62 comments

I think this is a docs issue, I think we changed this a little while back.

Maybe there should be a way to differentiate between initial loading (where you just show a loading spinner) and incremental loading (where you also want to keep the current content in addition to the loading indicator)?

@SachaG I think networkStatus is what you're referring to?

@helfer you're right, I was using an older version of Apollo that didn't have it.

Hi!
I'm not receiving any new props when I call refetch, only when the results arrive from the server (and only if there are new results)
This is bad because I can't show a loading indicator when the users request the refetch action
I tried to inspect networkStatus as @helfer pointed but it is always "7", and I have no idea what it means

Thanks!

I just gave @PascalHelbig's repro a quick check, swapping console.log('loading', data.loading); for console.log('networkStatus', data.networkStatus); and it only logs once:

networkStatus 1
networkStatus 7
refetch
refetch
refetch
(and so on)

If I instead change query to query a dummy resolver I added to my server just now to return a random number, it looks more like this:

networkStatus 1
networkStatus 7
refetch
networkStatus 7
refetch
networkStatus 7
refetch
networkStatus 7
(and so on)

The component never receives any props to indicate that a query is in flight.

Aha, I might have found a workaround / solution: there is a notifyOnNetworkStatusChange option on watchQuery, which is only documented in the API reference.

With that option set, I get this output:

loading true networkStatus 1
loading false networkStatus 7
refetch
loading false networkStatus 2
loading false networkStatus 7
refetch
loading false networkStatus 4
loading false networkStatus 7
refetch
loading false networkStatus 4
loading false networkStatus 7

This is workable, but definitely surprising behavior that definitely needs to be better documented and possibly revisited.

Some specific feedback of things that are surprising to me:

  • My original intuition of the loading value is that it would be true whenever a query was in flight.
    Instead, it seems to be true only on the initial load? (so it acts more like what I would call !loaded) Although I think I've seen it also be true when the query's variables were updated...?
  • As mentioned above, networkStatus only receives updates if you pass the notifyOnNetworkStatusChange flag.
  • networkStatus is a number which doesn't mean much of anything to me. The only place this is "documented" is, as far as I'm aware, the type definition in the source code.
  • The first refetch's networkStatus, according to that type definition, is setVariables, rather than refetch, which is what I would expect and what it is on subsequent refetches. Is this a bug?

+1

This is really just a docs issue, so we should fix it there.

I have this problem too. Whenever I make a call to .refetch, data.loading is always false and data.networkStatus is always 7.

We hit the same problem this time with error property, which does not get updated correctly on refetch (with spotty connection: stop internet connection → refetch, you got error → start internet connect → refetch, your component does not re-rendered, thus still has the same error as property).

The only workaround is to use notifyOnNetworkStatusChange but shouldn't this be the default if properties like networkStatus, error and loading are exposed to the child component?

I have submitted a PR with a small documentation change in the docs that addresses this.

Which version of Apollo Client are folks running?

I'm on [email protected]. The version of react-apollo is the current master because the latest published is a couple of weeks old and the current master has fixes.

Mine is:

"apollo-client": "^0.10.0",
"react-apollo": "^0.13.0",

any solution to this?

@aikonplus the "solution" is to pass notifyOnNetworkStatusChange: true as a query option. This is really a docs issue and should be fixed there. If someone could make a PR to the React Apollo docs it would be very much appreciated!

Loading actually changes to true whenever a refetch happens, but unless notifyOnNetworkStatusChange is set, the component doesn't get passed the update.

Sorry to bug you on, well, a closed bug, but how does one pass query options to refetch()?

You can't. It wouldn't make sense to pass most options anyway, because refetch returns a promise.

Wouldn't a more sane behavior be to set the loading flag to true, or at least add refetching flag?

I mean, imagine you have two buttons: Start/Stop, and a "loading..." indicator.

On first view, you'll see "loading..." and then the Start button.
When you click Start, you'll still see the Start button, which means (in slow networks) you can spam it with multiple clicks. Yes, you can lock it, but wouldn't it be nicer to see the "loading..." indicator instead?
After a while of remaining with the Start button, you'll suddenly see the Stop button.

Adding a refecthing flag, or even better - setting the loading flag (with notifyOnNetworkStatusChange: true by default for all queries), would make a lot of sense.

@helfer The notifyOnNetworkStatusChange approach doesn't seem to work with version 1.4.4, neither networkStatus nor loading was captured updating when refetch.
My code goes:

componentWillMount() {
    if (this.props.data.networkStatus >= 7) {
        console.log("refetch");
        this.props.data.refetch();
    }
}
componentWillReceiveProps(nextProps) {
    console.log(this.props.data.networkStatus, nextProps.data.networkStatus);
    console.log(this.props.data.loading, nextProps.data.loading);
}

Output:

1 7
true false

After refetchwas called, a new request appeared in network tab in Chrome devtools, but the output went:

1 7
true false
refetch
7 7
false false

And there's no more output after the request was completed.
Expected:

1 7
true false
refetch
7 4
false true
4 7
true false

Any solution for this? I have the same problem

Shouldn't this ticket be reopened as the feature does not work as expected, aka. no changes innetworkStatus after refecthing the query.

@eskimoblood I am having the same issue and I think that https://github.com/apollographql/apollo-client/issues/1263 https://github.com/apollographql/apollo-client/issues/1622 is also related to this problem. We should probably create a new issue for this with a 2.0 prefix. What is your opinion?

Edit:

Seems like the promise returned by refetch is resolved after the refetch is finished, which means this can be used as a temporary workaround for determining if a query is being fetched or not.

Edit 2:

When using refetch while loading is true, the promise of refetch will never resolve nor reject.

I will also try to take a look at the code on the weekend.

Any response from the maintainers?

I don't know if anyone is still having issues with this, but I solved it by changing my fetchPolicy option to cache-and-network in the options passed to the graphql hoc function.

Im still having this issue, and I set the notifyOnNetworkStatusChange flag like @dallonf said, but I still get networkStatus=7 everytime.

Also always getting networkStatus of 7 with fetchPolicy: 'cache-and-network' and notifyOnNetworkStatusChange: true..........

This is not working for me either. Any solution to this?

"apollo-boost": "^0.1.10",
"apollo-cache-persist": "^0.1.1",
"graphql": "^0.13.2",
"react-apollo": "^2.1.9",

Yeah this still isn't working for me. It's actually completely breaking my app. Seems like a pretty fundamental thing.

Same problem here. We need to update component rendering base on loading props.
When we refetch a query (after a mutation with refetchQueries options) we need to show a loading status in our component

How do we get this reopened?

IMO it is not addressed properly

@helfer please could you take another look at this thread - we still think there is issue but it seems to have been abandoned by maintainers

I was able to resolve this by setting notifyOnNetworkStatusChange to true and fetchPolicy to cache-and-network. Kinda strange that I had to set fetch policy too. Isn't cache-and-network the default?

Anyway, this is working for me in version 2.3.0:

<Query
  query={MY_QUERY}
  variables={{...}}
  notifyOnNetworkStatusChange={true}
  fetchPolicy={'cache-and-network'}
>
  ...
</Query>

And I'm triggering refetch from another query by using refetchQueries prop.

This definitely still feels like a bug to me. You shouldn't have to set a fetchPolicy in order to get notified of network status updates.

What I have found is that if a query is successful and you use the option of notifyOnNetworkStatusChange set to true, then when you call refetch, you will get the updated props with networkStatus of 4 while that request is in flight. The bug that I'm seeing is that when you receive an error from the request and you have notifyOnNetworkStatusChange set to true and try calling refetch, then you will not get the updated props that include a networkStatus of 4.

This needs re-opening.

Having to explicitly set two props and faff around with numeric networkStatus values, just to get an indication of a component re-loading is definitely not the assumed or logical behavior.

Irrespective of fetch policy, IMO the assumption is that a component-specific refetch() would _always_ request new data from the network. It ought to be possible to override that fetchPolicy too explicitly in the call to refetch().

The gymnastics of having to parse network status and set an external, parent-level loading-style bool makes the current implementation far more difficult to work with.

In the interim, you can set notifyOnNetworkStatusChange={true} and simulate a loading like this (in TS):

import { NetworkStatus } from "apollo-client";

// ... 

{({ data, refetch, networkStatus }) => {
  const loading = networkStatus !== NetworkStatus.ready;
}

Surprising that, with the amazing push forward in Apollo's products in general, this issue is still causing problems to developers - 2.5 years later.

@leebenson I was using a workaround similar to what you described with

const loading = networkStatus !== NetworkStatus.ready;

But that seems broken in the scenario when an error occurs and then you call refetch. What I have noticed is that the networkStatus never updates when refetch is called after an error (you only get notified when the response comes back and networkStatus is 7 still). I made a new issue (#2926) specific to that situation, but I would agree that this issue should be re-opened as well.

Same issue, networkStatus is 7 (NetworkStatus.ready) when call refetch.

@Collin3 That's really the issue in my case. My current workaround is to manage the networkStatus myself.

I have a little <QueryStatus networkStatus={networkStatus} /> component. Then in the component I have something like

let [networkStatus, setNetworkStatus] = useState(props.networkStatus);

useEffect(() => { setNetworkStatus(props.networkStatus); }, [props]);

function doRefetch() {
  setNetworkStatus(4);
  refetch();
}

With that, I always have the correct networkStatus wether updated by apollo or by me.

Why is this issue closed? :/ I still have this problem!

@stearm Did you try solution I mentioned few comments above?

That one is working for me with vanilla <Query>, however I was just trying it in react-apollo-hooks, and it doesn't work there! Don't have time for deeper look– but has anyone made this work with hooks?

I'm using the HOC approach and it also doesn't work (refetch is triggered by another component).

export default compose(
    graphql(QUERY_GET_OWN_COLLECTIONS, { name: "QGetOwnCollections", options: { notifyOnNetworkStatusChange: true, fetchPolicy: "cache-and-network" } })
)(SidebarContainer)

Same issue, seems quite fundamental to me too.

apollographql/apollo-client#4992 may help solve this. If anyone can provide an up-to-date reproduction, I can verify that. Thanks!

I stumbled upon this issue within 5 minutes of trying out react-apollo.

It only fails to rerender the component with {loading: true} if the last fetch resulted in an error.

From a product/user perspective, this means the following:

If the user is given a retry button after fetching has failed (several times), and the user then clicks the button:

There is simply no way of informing the user that a request is actually being retried. If it fails again, it looked like nothing ever happened.

This would create pretty big UX issues in a lot of frontends.

fetchPolicy={'cache-first'} is the only workaround that works, but that doesn't work for hooks...

@kasvtv sorry to hear that this is still an issue for you. Could you provide a codesandbox.io or github repo that shows this problem? I really want to fix this bug!

@jasonpaulos
This problem happens very consistently any time the last result was an error.

I am trying to get apollographql/apollo-client#4992 merged into apollo-client, which should solve this issue @kasvtv

Wow this is quite similar to #2177 i.e. issue closed/ignored even though it persists, and also related to network status changes.

Thank you very much for https://github.com/apollographql/apollo-client/pull/4992 @jasonpaulos! We'll prioritize a release of Apollo Client with those changes included, so this issue will be fixed in RA very shortly.

This still occurs on v2.6.8 for me. I think i have spent an honest 4 hours trying to fix this bug where

<Query 
variables={{this changes}}
fetchPolicy='network-only'/>
// Check network status, it's always 7
// Data is fulfilled twice, once with older param second with newer param, causing intermittent ui flashes of data
</Query>

I was able to resolve this by setting notifyOnNetworkStatusChange to true and fetchPolicy to cache-and-network. Kinda strange that I had to set fetch policy too. Isn't cache-and-network the default?

Anyway, this is working for me in version 2.3.0:

<Query
  query={MY_QUERY}
  variables={{...}}
  notifyOnNetworkStatusChange={true}
  fetchPolicy={'cache-and-network'}
>
  ...
</Query>

And I'm triggering refetch from another query by using refetchQueries prop.

ApolloClient v2.6.3
Savior Comment by @vojto , Just don't think just copy this word to word and add networkStatus checks.

If you're using React, the new apollo hooks useQuery have fixed these issues. I've actually removed my above workaround from my codebase.

@Dajust do you mean for @apollo/react-hooks v. 3.1.3? loading still doesn't change for me when I call refetch on that version

I fixed this issue by adding notifyOnNetworkStatusChange: true,, which triggers loading to change even when calling refetch

  const { loading, data, fetchMore, refetch } = useQuery(GET_POSTS, {
    notifyOnNetworkStatusChange: true,
    fetchPolicy: "network-only",
  });

However, it causes fetchMore to trigger loading as well

I fixed this issue by adding notifyOnNetworkStatusChange: true,, which triggers loading to change even when calling refetch

  const { loading, data, fetchMore, refetch } = useQuery(GET_POSTS, {
    notifyOnNetworkStatusChange: true,
    fetchPolicy: "network-only",
  });

However, it causes fetchMore to trigger loading as well

This solution works but is it possible not to trigger loading on fetchMore ?

@EdmundMai Doesn't work for me (@apollo/client 3.0.0-beta.50), the loading is false when I call refetch. Doesn't work on 3.0.0-rc.2 too. Also the networkStatus is always undefined. I use:

const {
    refetch: checkCredentials,
    data: isValidCredentialsData,
    loading: loadingIsValidCredentials,
    networkStatus,
  } = useQuery<isValidCredentialsQuery, isValidCredentialsQueryVariables>(
    IS_VALID_CREDENTIALS_QUERY,
    {
      skip: true,
      fetchPolicy: 'network-only',
      notifyOnNetworkStatusChange: true,
    },
  )

and then I call checkCredentials. Maybe it's because I use skip: true?
I need it to get a promise that I can await (useLazyQuery doesn't return a promise).

I'm seeing the same behavior as @EllaSharakanski, using [email protected].

If I remove skip: true then the refetching works as described by others above, but I added skip to prevent the query from running the query too early; I don't see why skip should interfere with loading, data, or networkStatus when refetching later.

I fixed this issue by adding notifyOnNetworkStatusChange: true,, which triggers loading to change even when calling refetch

  const { loading, data, fetchMore, refetch } = useQuery(GET_POSTS, {
    notifyOnNetworkStatusChange: true,
    fetchPolicy: "network-only",
  });

However, it causes fetchMore to trigger loading as well

This solution works but is it possible not to trigger loading on fetchMore ?

For anyone who wants to have different loadings on fetchMore and refetch, watch the networkStatus as it has value of 3 on fetchMore and 4 on refetch.

Was this page helpful?
0 / 5 - 0 ratings