React-apollo: `loading` stuck true after variable change

Created on 25 Aug 2016  Β·  55Comments  Β·  Source: apollographql/react-apollo

I have the following problem: when the props of my GraphQL-connected component change, and I use one of the props in the options (as variables), loading is stuck to the value true whenever both the previous value and the current value resolve to no result.

Here is an example:

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

// Display props.
const Demo = (props) => (
  <pre>{JSON.stringify(props, null, 2)}</pre>
);

// Connect to some GraphQL query.
const ConnectedDemo = graphql(gql`
  query getStuff($name: String!) {
    stuff(name: $name) {
      payload
    }
  }
`, {
  options: ({ name }) => ({ variables: { name } }),
})(Demo);

// Set up client.
const client = new ApolloClient(/* ... */);

const render = (props) => {
  ReactDOM.render((
    <ApolloProvider client={client}>
      <ConnectedDemo {...props} />
    </ApolloProvider>
  ), document.getElementById('main'));
};

// Initial render.
render({
  name: 'does-not-exist',
});

// Prop update: fetch.
window.setTimeout(() => {
  render({
    name: 'nothing-either',
  });
}, 1000);

Here, during the initial render, everything is fine: Demo receives { loading: true, stuff: null }. After some milliseconds, this is changed to { loading: false, stuff: null }.

However, when the prop is updated, Demo receives { loading: true, stuff: null } and it gets stuck there, even though the network request completed as did the first one.

The problem happens with react-apollo version 0.4.7 as well as 0.4.6. It does not happen whenever the first fetch did return an object (i.e. stuff was something other than null).

bug

Most helpful comment

Just found this solution, over on https://github.com/apollographql/apollo-client/issues/1186 ... set notifyOnNetworkStatusChange to true - it works!

All 55 comments

@sgoll I can't seem to replicate in a test? https://github.com/apollostack/react-apollo/pull/173

Can you create a repo or a failing test?

@jbaxleyiii Sure. I created a failing mock demo at https://github.com/sgoll/react-apollo-170. Please let me know if that helps.

Fixed with #191

I'm having this error after last update. had to downgrade.

I've been running into the same issue. Could this be related to query response batching somehow? Maybe it's a coincidence, but I've noticed that when the infinite situation happens, the query response is always sent back from the server as a stand-alone result.

Whereas in scenarios where everything works as expected, it's always batched with the results of another query (I happen to have multiple separate query containers on the same page).

I highly doubt that this is related to batching. More likely it has something to do with fragments, where Apollo Client gets the result from the server, writes it to the store, then reads it back, but still thinks it only has partial data, and thus doesn't notify the observer.

If someone can provide a reproduction, we should be able to get this fixed pretty quickly @hiei189 @SachaG

Is there a guide on how to best provide reproductions? I seem to remember there was a sample app you could use?

@helfer @SachaG
here is the repo to reproduce the issues we talked above
https://github.com/comus/react-apollo-issue
https://cl.ly/2b2D3J2A1e13

@thebigredgeek I think this is a reproduction of the same issue that we tried to debug together a while back. Can you take a quick look at the video and tell me if you think my assessment is correct?

The problem here is also due to a discrepancy between the observer.next result and the currentResult. Interestingly @comus says that it was introduced with react-apollo 0.11.0 and didn't occur before.

If you want some mystery to solve, this could be an interesting puzzle. I took a quick look but couldn't get to the bottom of it yet, and I won't have time to look again before the weekend.

@helfer yeah this is the EXACT issue, though I haven't encountered it in a while (ever since rebuilding the product).

geez, this is the issue that will never end πŸ‘Ž

@helfer @thebigredgeek I setup an error repor https://github.com/apollographql/react-apollo/pull/666

If I have to load two queries with different variables (just different in one key) and use the { branch } from recompose to get around the loading, somehow it will get stuck in loading as well but only between loading of pages that has already been loaded (using nextjs). Which means it is loading the page in the client only.

Had to downgrade to 0.8.3 to remove the infinite loading.

Alright, small update: I've figured out that for the reproduction by @comus the issue doesn't occur without recycling observable queries. It's pretty hard to track down what the exact issue is, but it must have something to do with parameters not correctly being updated when a query is recycled.

That's all I know so far, I'll keep poking around until I find the bug. 🐞

Ok, the saga continues (writing down some notes for myself):

I found out last time that the result that's passed into react-apollo in the next call was different from what you got when currentResult was called after that. In next loading is false, but in currentResult, loading is true again. That's what happens for the last result, which is why things are stuck in loading.

However, the real mystery is _why_ next is even fired on that query, because the APOLLO_QUERY_RESULT action for the postsList query of user 3 hasn't even been dispatched at that point, which means that there's no result in the store. Something else must be firing the observer, and it must erroneously be thinking that it already has the result, most likely due to the fact that it's a recycled observable query.

Sadly next is not called again when the APOLLO_QUERY_RESULT is finally dispatched. I believe that this is because the result is referentially equal to the result that was erroneously produced earlier. I confirmed this by changing the posts list of the second user to contain one result instead of zero (user 3 has zero posts). If user 2 has one post, the query does NOT get stuck in loading.

Next, I will try to find out why observable.next is called even when APOLLO_QUERY_RESULT hasn't been dispatched yet. My guess is that if I can fix that, a lot of the "stuck in X" issues will go away.

That said, I believe that the root cause of this issue is that react-apollo decides to re-render when observable.next is called, but then calls currentResult to get the data. Having these two code paths makes it really hard to figure out where data is coming from, and it makes it really easy to introduce consistency bugs of this kind. I'm going to try and make sure that Apollo Client's core does not have such issues once we've refactored it for v2.

@helfer did you see the failing test for this via #666?

@jbaxleyiii yeah, the test looks good, but I think this is a bug in Apollo Client, so I'll have to fix it there first and write a test for it.

I'm getting pretty close though! I've figured out that the problem is that queryStoreValue.variables is the old variables when it should be the new ones. Now I just need to figure out why the variables don't get updated correctly in the store.

update: the value in the store is correct, but that's not where queryListenerForObserver gets its queryStoreValue. It comes from broadcastQueries. broadcastQueries just reads them from the store, but there must be some sort of race condition here that I'm not seeing, which causes that to be the old value... :confused:

Last update for today: I still don't know how that race condition can exist, but I was able to get @comus reproduction to work by reading the queryStoreValue from the store in queryListenerForObserver. I might just push that fix over the weekend if I can't track down the real source of this race condition.

Just wanted to say thanks for all the detective work!

Wanted to +1 this same issue, and thanks for digging in @helfer - my query is getting stuck with loading: true whenever a variable changes, even though I'm actually getting back all the data for the new request from the server. Happy to chat or share more info about what's going on.

This issue has been automatically marked as stale becuase it has not had recent activity. It will be closed if not further activity occurs. Thank you for your contributions to React Apollo!

I see the bot marked this as closed, but I am still experiencing this issue.

Some background info: I am using next.js with react-apollo. Similar to the with-apollo example in the nextjs repo. I haven't experienced this in the first few weeks of working on this project up until the point where I had to get parameterized queries with variables changing based on page route.

When the component mounts for the first time everything works as expected but when navigating back and then going to the page again with a new ID (browsing a new post) the query resolves to loading true and no data in it, but when checking in the redux store the value is actually there and the http request did return the full object.

For those who stumble upon this, I've solved my issue by specifying a name. It seems like name field is required when using options. It's pretty weird behaviour, but if it works it works.

@cyrus-za Could you please share your code. I can not make it work.
Thanks.

 graphql(gql`
    query allUsersQuery {
        allUsers {
            id
        }
    },
`, {name: 'allUsersQuery'}),

Note that allUsersQuery is in 2 places. Those names need to match for some unknown reason

PS. This example doesn't have variables but you get the idea.

@cyrus-za Thank you very much for mentioning that the names have to match. Saved my day!
If this issue is not fixed I think it should at least appear in the docs somewhere.

When the component mounts for the first time everything works as expected but when navigating back and then going to the page again with a new ID (browsing a new post) the query resolves to loading true and no data in it, but when checking in the redux store the value is actually there and the http request did return the full object.

I'm seeing exactly this.

For those who stumble upon this, I've solved my issue by specifying a name. It seems like name field is required when using options. It's pretty weird behaviour, but if it works it works.

I don't see how or why that would help, since that should simply rename the data prop?

@poyannabati my guess would be that the data prop matching actually triggers a new render in the react component which loads the new props, but yeah it is fairly strange

same issue here

I am having the exact same issue here, renaming the query does not solve the issue, a full page refresh does work though.

Upgrading to the latest version seemed to solve it for me.

On Wed, 6 Sep 2017 at 04:48, Morphexe notifications@github.com wrote:

I am having the exact same issue here, renaming the query does not solve
the issue, a full page refresh does work though.

β€”
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/apollographql/react-apollo/issues/170#issuecomment-327359272,
or mute the thread
https://github.com/notifications/unsubscribe-auth/ABbfqrxh6I3pbTEXwjjW7T10NDnR48ILks5sfggZgaJpZM4Js-Q8
.

I can reproduce the issue on [email protected]. It happens when unmounting the component with a variable x and mounting it again with a variable y.

Having the same issue, has there been any progress resolving this one?

same issue ! any working on this?

I have been moving my apollo HOC one level higher in the react tree to dodge the issue. But it comes with some tradeoff. I wish I could put it as close as where the data is needed. @kien8995 The issue has been open for a year and have around 20 people involved. I'm sure the maintainers are aware of this issue. So either they miss a reproduction example and I understand it. Or there is a much larger core issue related to the design of the project.

Also just ran into this issue... tried the naming workaround but couldn't get that to work. @oliviertassinari could you describe your workaround? not sure I followed

Something I've noticed with this issue is that the query that causes loading to become stuck is trying to fetch from the network, when the results should be drawn from the cache. If I navigate away from the screen that is stuck loading, and I go back to it then the results load from the client cache and everything is fine.

In Redux, the events are: INIT, RESULT, <nav to screen B>, INIT, RESULT, <nav back to screen A>, STOP, INIT, RESULT

What I believe they should be: INIT, RESULT, <nav to screen B>, STOP, INIT, RESULT, <nav back to screen A>, STOP, INIT, RESULT_CLIENT

The two issues you can see here:

  1. STOP is not fired when navigating to screen B
  2. navigating back to screen A causes a network call, and fires RESULT instead of RESULT_CLIENT

Just found this solution, over on https://github.com/apollographql/apollo-client/issues/1186 ... set notifyOnNetworkStatusChange to true - it works!

thanks @mattfysh

Where do you set it? inside the constructor of ApolloClient or inside each and every query decorator?

@cyrus-za I'm setting it on just the affected query component right now

thanks @mattfysh πŸ‘ it works !

Thanks @mattyfish

On Tue, Sep 19, 2017 at 3:25 PM Kien Tran notifications@github.com wrote:

thanks @mattfysh https://github.com/mattfysh πŸ‘ it works !

β€”
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/apollographql/react-apollo/issues/170#issuecomment-330537974,
or mute the thread
https://github.com/notifications/unsubscribe-auth/ABwqZeUa1cUPNRScu91I8LyB71_d20tHks5sj8C8gaJpZM4Js-Q8
.

notifyOnNetworkStatusChange should really be on by default. Does anyone know why it isn't? Found this very surprising and frustrating.

This issue has been automatically labled because it has not had recent activity. If you have not received a response from anyone, please mention the repository maintainer (most likely @jbaxleyiii). It will be closed if no further activity occurs. Thank you for your contributions to React Apollo!

This issue has been automatically closed because it has not had recent activity after being marked as no recent activyt. If you belive this issue is still a problem or should be reopened, please reopen it! Thank you for your contributions to React Apollo!

I have this same problem here. Any progress in this issue?

I just ran into this issue as well. Setting notifyOnNetworkStatusChange: true worked for me as well.

Still having this issue with react-apollo 2.04, and setting notifyOnNetworkStatusChange: true does nothing for me in this case.
Instead the client simply throws the network error (e.g. the 404 that one of my field resolvers returned) and loading stays at true.

Just hit this bug as well. My scenario is very similar to that of @cyrus-za and @oliviertassinari. I managed to isolate the kinds of updates that result in loading being stuck to true and data not updating; Maybe the following will help someone with more intimate knowledge of Apollo internals track down the bug.

We are using react-apollo 1.4.16 and apollo-client 2.3.5.

Our scenario:
We use react router 4 and each page is wrapped in graphql(). When the page mounts, the query is executed with variables supplied to the page. We don't currently use id-based cache normalization, but the default query path based one.

I isolated 3 conditions that are necessary for it to be stuck:

  1. I navigate away from a page and then back again with a different set of variables supplied to the page.
  2. The query results are the same as those from the previous page visit. If the results are different there is no problem.
  3. There cannot be another page visit between the two, which returns a different query result. For some reason, if I navigate away, then back, and the resulting query returns a different result than the previous, then even if a subsequent page visit does lead to the same query result as the first one, it works.

Like others have noted before me, the queries execute without errors, the data is in the cache, it is just that when the data is loaded my page is not updated by the graphql() HOC. And notifyOnNetworkStatusChange: true did work for me, probably because it triggers updates to the component even if data loading doesn't.

I can confirm that this bug occurs on

β”œβ”€ [email protected]
β”œβ”€ [email protected]

when conditions above specified by @andrewpeterprifer are met.

Setting notifyOnNetworkStatusChange: true solves the issue. The sad part of this bug is that it's so "low level" that I even haven't thought that it may be a bug in apollo thus I spend quite big amount of time debugging my own code...

Maybe it'd be worth to write about this issue in the official docs?

Any progress on this? Ran into the issue as well and setting notifyOnNetworkStatusChange can't be the answer.

use the new Query Component and dont do a query while exporting the component:

avoid something like this

export default withApollo(graphql(myQuery, {
    options: () => ({
        notifyOnNetworkStatusChange: true
    })
})(myComponent))

you will also face some bugs with the fetchPolicy settings that will just not work here.

e.g. you load a component and you revisit it but you get no updates.

Base on the NetworkStatus Codes apparently the loading state come from networkStatus < 7 but that state is not reliable, a simple solution could be:
set your own loading variable, like this.

 <Query
      notifyOnNetworkStatusChange={true}
     query={/**/}
>
  {
    ({networkStatus, error, data}) => {
       let loading = !(networkStatus === 7 || networkStatus === 8);
       if(loading) return <h1>Loading:  { `${loading }` }</h1>
       return <h1>{data.name}</h1>
     }
   }
</Query>

be aware the code 7 mean that there is not query in the fligh and everything is fine (Like the meme), and the code 8 mean that there is not query in the fligh but got an error, so in both cases the request is complete, negating both cases !(networkStatus === 7 || networkStatus === 8) mean that the request is not complete and can consider the state as loading

FYI: I am able to recreate this issue with "react-apollo": "2.5.5" - notifyOnNetworkChange solves the problem, but obviously not ideal.

I've had the same issue.

Since it only happens when my back end sends the same response twice in a row, I make sure it never sends the same response twice in a row. I happened to have a "hello(name:S)" method which returned "Hello, S", which we had been using for early testing.

So I changed my query from
query Q($x:String) { stuff_I_want(x: $x) { name } }
to
query Q($x:String, $uniquification: String) { stuff_I_want(x: $x) { name } avoid_deduping: hello(name: $uniquification)

Then I generate a unique string β€”Β for my application, the millisecond clock is plenty unique β€” and pass it as $uniquification.

Was this page helpful?
0 / 5 - 0 ratings