apollographql / react-apollo

:recycle: React integration for Apollo Client
https://www.apollographql.com/docs/react/
MIT License
6.85k stars 787 forks source link

loading is false, but no data is present (and no error) #1314

Closed adamdonahue closed 5 years ago

adamdonahue commented 7 years ago

Just upgraded to Apollo 2.0, using react-apollo 2.0.0 -- it now appears that the graphql props option for a query is passed a data argument that can have loading set to false but no data.

That is, given a query, say:

query X($id: ID!) {
  myItem(id: $id) {
    id
    field
  }
}

and a graphql call:

graphql(query, {
  props({ data: { loading, error, myItem } }) {
    console.log('loading =', loading);
    console.log('error =', error);
    console.log('myItem =', myItem);
    return { ... }
 }
})(MyComponent)

We're seeing the output:

loading = true
error = undefined
myItem = undefined
loading = false
error = undefined
myItem = undefined
loading = false
error = undefined
myItem = {<data>}

That is, there is an interim state between loading having been set to false but myItem yet available on the data argument.

Is this expected, as it appears to differ from the behavior of Apollo 1.0. And if so, what is the reliable way to check that data was loaded and is ready for use in the component?

We're now using (loading || !myItem) to check whether something is loading.

Panoplos commented 7 years ago

I can confirm this issue exists.

loading false

no data

Offending code:

 graphql(
    gql`
      query localise($locale: LocaleType!, $resourceIds: [String]) {
        l10nSlugs(locale: $locale, resourceIds: $resourceIds) {
          id
          resourceId
          slug
        }
      }
    `,
    {
      name,
      options: ({ locale }) => ({
        variables: {
          locale,
          resourceIds
        },
      }),
      props: props => ({
        l10n: {
          /**
           * Whether or not the graphql resource is loading.
           */
          loading: props[name].loading,
          /**
           * Conveniently offer the locale.
           */
          locale: props.ownProps.locale,
          /**
           * And the BCP47 normalise version.
           */
          bcp47Locale: normaliseBCP47Tag(props.ownProps.locale),
          /**
           * Localise a slug with insertions.
           * @param {string} resourceId Resource ID of the slug to localise.
           * @param {string} slug Default slug in case loading.
           * @param {Array<string> | Object: []} insertions Dynamic content for the slugs.
           * @returns {string} Localised slug.
           */
          localise: (resourceId: string, slug: string, insertions: [string] | Object = []): string => {
            if (!props[name].loading && !props[name].error) {
                           ^^^^^^^^^^ Checked here...
              for (let s of props[name].l10nSlugs) {
                                        ^^^^^^^^^^^ BAM!
                if (s.resourceId === resourceId) {
                  return insert(s.slug, insertions)
                }
              }
            }
            return insert(slug, insertions)
          },
          /**
           * Locale aware ordinals (e.g. 1st, 2番目)
           * @param {Number} ord The numerical ord to convert.
           * @returns {string} The locale-specific ordinal
           */
          ord2str: (ord: Number): string => props.ownProps.l10nShared(`${ord}ord`),
          /**
           * Get whether or not the current locale arranges the name Last, First.
           * @returns {boolean} True if the locale requires a Last, First name arrangement.
           */
          nameLastFirst: (): boolean => NameLastFirstLocales.includes(props.ownProps.locale),

          /**
           * Gets teh minimum number of characters allowed in the locale's name part.
           * @returns {number} The minimum number of characters in the current locale.
           */
          minCharsInNamePart: (): number => MinCharsInNamePart[props.ownProps.locale]
        }
      })
    }
  )
malexandre commented 7 years ago

Yeah, got the same error when trying to pass my company project to the last version, was previously in 1.4.16.

Panoplos commented 7 years ago

Is anyone looking into this? This is a serious bug that is killing quite a bit of my code, now.

javascriptlove commented 7 years ago

Can confirm this happens now after i updated today to apollo-client@2.0.1 and react-apollo@2.0.1

UPD: some investigation/debugging led to this line in apollo-client package, giving null in the result,

https://github.com/apollographql/apollo-client/blob/f172ee6b37ab66d627b317d9b9f6ac1b8a739e00/packages/apollo-client/src/core/QueryManager.ts#L418

screen shot 2017-11-13 at 14 51 46 screen shot 2017-11-13 at 14 51 25

jbaxleyiii commented 7 years ago

@adamdonahue I'll take a look at this! Still working through issues after launch + took a week off last week! Thanks all for the issue report!

javascriptlove commented 6 years ago

Btw, i've resolved that on my side by setting fetchPolicy to cache-and-network, no more errors after that. So seems like the error is there only with network-only

Panoplos commented 6 years ago

@javascriptlove Actually, the issue exists with the default setting, too.

moimael commented 6 years ago

Any workaround that doesn't require adding a check on data for all loading queries ?

peggyrayzis commented 6 years ago

@adamdonahue Can you please provide a stripped down reproduction for us to test? I've tried to reproduce with our test app Githunt, but haven't been able to.

malexandre commented 6 years ago

I just tested with the 1.0.3 of apollo-client, and everything's work for me. Thanks a lot @jbaxleyiii.

jbaxleyiii commented 6 years ago

This has been confirmed as fixed with 1.0.3 in apollo-client! Thank you all for the reporting and helping to diagnose and fix this one!

Panoplos commented 6 years ago

Confirmed fixed in 2.0.1.

j4mesjung commented 6 years ago

EDIT: Fixed on my end by just adding an id to every query level. However, still unsure why some id's returned the data but some did not...might have bad data on my end. Will continue to debug.

hi @jbaxleyiii, @peggyrayzis i am using graphcool service to host my data and all my queries work in their playground when I pass in the given ids. However, on the client side, some queries will correctly return the data and some will not return any data even if loading: false. I am on apollo-client 2.0.4 screen shot 2017-12-15 at 11 24 19 am

after some digging around, the data is coming in correctly into newData.results. However, isMissing resolves to true from this line isMissing = !newData.complete ? !newData.complete : false; because newData.complete comes in as false. This then ultimately resolves into this line that then takes the data from lastResult which is undefined and then resultFromStore.data returns undefined. Is there something wrong in my query that makes complete resolve to false? Not exactly sure what is happening here and would like some input if possible. Thanks!

debug1

crokobit commented 6 years ago

still have this issue with version '2.0.4' here 😢 . Sometimes it will work with safari but it constantly failed with chrome.

j4mesjung commented 6 years ago

@crokobit what do you mean by working in safari and failing in chrome? Are you talking about the data.loading: false but not seeing the expected data?

shoooe commented 6 years ago

I'm on 2.0.4 and the render function on a component is called 3 times, 2 of which have loading = false and data = null and the last one has the data.

screen shot 2017-12-20 at 03 37 30

What are those extra 2 renderers for?

crokobit commented 6 years ago

@j4mesjung yes~

ssahni commented 6 years ago

@jbaxleyiii @peggyrayzis This issue still occurs for me.

I am using react native and

Interestingly enough, the issue only arises when I introduce 'id' as a field in my query. If I do not specify 'id' in my query, then everything works as expected.

Render flow goes like this:

  1. loading: true, no data
  2. loading: false, data is available and good
  3. loading: false, no data

Hope this helps!

denkristoffer commented 6 years ago

I'm seeing this too. If it helps, I know the data is already in the store and the request returns the correct data. It seems to happen when I render multiple components with queries at the same time?

xcv58 commented 6 years ago

@denkristoffer do you have any reproduce repo? I can't reproduce this in my branch: https://github.com/xcv58/react-apollo-error-template/tree/2591adeeb9401fb90dcb28ed84c44d53ed58285a

pencilcheck commented 6 years ago

I have this issue as well, it is a very simple query but it just never return any data when loading is false and error is undefined.

"apollo-client": "2.0.4" "react-apollo": "2.0.4"

However it returns very quickly when using graphiql

------------------ update ---------------------

Fixed it by adding id to every type in the query, like literally every type mentioned in affected query, otherwise it will not work...

jdachtera commented 6 years ago

I have the same problem after running a mutation which returns updated data.

felixk42 commented 6 years ago

Can confirm I am having the same problem, used to work when id is attached to every queries, but now it doesn't. Seems to be triggered by a mutation that adds something.

w9 commented 6 years ago

I have the same problem as well. (apollo-client@2.2.5)

This is a serious bug and this issue should be re-opened.

DennisBaekgaard commented 6 years ago

I got this issue as well, but was able to fix it. In my case it was mostly because the error messages aren't totally clear. I had a mutation which did not return all properties of the entity, this in turn caused the list of entities I was mapping over to be undefined. When I retrieved the last property (albeit being 'null') the data was correctly injected into props.

This 'gotcha' could probably be either documented more clearly on the mutation / updating the store pages, or indicated more clearly when there is an error in the console. It seems counter-intuitive when the idea of a graph setup is to retrieve only the properties which are important to you, and not overfetch, when it in turn gives an error in Apollo.

finnigantime commented 6 years ago

^ got this same issue, and this workaround worked for me as well (have the mutation return the same complete object subfragment as the original query). Totally unintuitive with graphQL and difficult to manage because different queries in our app return slightly different subfragments. Also no error or warning here, so I just ended up getting data.foo === undefined and data.loading === false which was a pain to try and debug.

davidalekna commented 6 years ago

I've just experienced this issue as well, but only when I test my app on chrome with network throttling, so it may appear when the network is slower or something...?

grydstedt commented 6 years ago

This is breaking a lot of things for us. Any known workaround?

nadeesha commented 6 years ago

@grydstedt The best workarounds I've found:

  1. Include ids in the graphql objects whenever you request them. (This helps apollo-client resolve cache better)
  2. When you do a mutation , try to return the same graphql selection from mutations, as the query you did.

In no way am I suggesting these are good workarounds. But they are workarounds nonetheless.

FezVrasta commented 6 years ago

@nadeesha adding id fixed it for us, still annoying tho... Thank for the workaround.

MJones180 commented 6 years ago

I am just running into this issue and I don't have the ability to add id to every field. Any ideas for an alternate fix? I am using apollo-boost. Thanks

stevenfitzpatrick commented 6 years ago

Getting this issue as well...the query seems to run fine with loading: false, but the data just remains null even though in network request I can see the query has returned data.

pitou commented 6 years ago

The only workaround that "solved" the problem for me was setting fetchPolicy: 'no-cache'. I hope this will help.

andfs commented 6 years ago

The error is not in react-apollo, but in apollo-client. I'm using it with angular 4 (apollo-client version 2.2.8) and got the same error. The only workaround was to set fetchPolicy: 'no-cache' just like said @pitou

javorosas commented 6 years ago

This issue disappeared for me after migrating from apollo-client-preset to apollo-boost and react-apollo@2.1.3

ZiXYu commented 6 years ago

still have the issue, why it was closed? It comes when we try to fetch a query twice in the same time.

pierrebiver commented 6 years ago

I do have the same error using apollo-client 2.2.8 and apollo-cache-inmemory 1.1.12.

javorosas commented 6 years ago

UPDATE: I'm seeing this strange state again. it occurs after I update the cache using data from an optimisticResponse. I'm thinking that maybe it is caused when you update the same query in the cache simultaneously. Maybe it creates a race condition of some sort.

amannn commented 6 years ago

I also just saw this with react-apollo@2.0.4 and apollo-client@2.2.0 (and earlier versions). I'm not able to reproduce it reliably though. Any chance we could re-open this @jbaxleyiii?

fuksito commented 6 years ago

I experience this issue with react-apollo 2.1.3 and apollo-boost 0.1.4

nicolasg-rocketleap commented 6 years ago

Still having this issue with latest versions, can we re-open this issue?

felixk42 commented 6 years ago

Second that, this bug has costed us at least 10 person-days for our little project, which has a total of ~200 person days spent on it...

calebdel commented 6 years ago

Experiencing this issue with react-apollo@2.0.4 and apollo-client@2.2.7

steelbrain commented 6 years ago

Fix is up in https://github.com/apollographql/react-apollo/pull/2003

adrianschneider94 commented 6 years ago

I have the same problem -- I give my component new props, loading is false but the data variable of the render prop function is empty. However, when rendering the first time, it works. My query is the following:

query FeedQuery($first: Int, $offset: Int, $filters: [FilterInput]) {
  datasheets(first: $first, offset: $offset, filters: $filters) {
    id
    productType
    articleId
    excelPath
    weight
  }
}

Interestingly, when I exlude the $filters variable, it works. $filters is a list of an input type (FilterInput) which consists of simple scalars. Do array fields need special care? The query component looks like this:

<Query query={FEED_QUERY}
        variables={{first: this.props.first, offset: this.props.offset, filters: this.props.filters}}
        >
    {({loading, error, data}) => {
        if (loading) {return "Loading"}
        if (error) {return "Error"}
        if (data.datasheets.length === 0){return "No result"}
        return (
            <List>
                {
                data.datasheets.map(article =>
                <ListItem key={article.id}>
                    <ArticleCard article={article}/>
                </ListItem>
                )}
             </List>
        )
    }}
</Query>

My versions are

steelbrain commented 6 years ago

@adrianschneider94 Can you test this with my patch from #2003? You can test by replacing react-apollo with @steelbrain/react-apollo in your project temporarily

adrianschneider94 commented 6 years ago

@steelbrain Thanks! The only problem left is that it hangs sometimes in the loading state depite the server returned data. I can‘t really see a pattern but I also don‘t know if the problem is even related.

nikogosovd commented 6 years ago

With all the workarounds suggested here, this behaviour is still a bug (48 comments). It is now the main source of pain in process of migration from 1.0 to 2.0.

@jbaxleyiii Are you going to do something with it?

UPD: I have this issue only while { fetchPolicy: 'no-cache' }.

adrianschneider94 commented 6 years ago

In this state react-apollo is hardly useable - I can't manage to make a simple query work. I also tried with React-Apollo 1.4 - but no luck. I switched now to URQL where this issue doesn't exist.

felixk42 commented 6 years ago

@adrianschneider94 seems related to https://github.com/apollographql/react-apollo/pull/2003#issuecomment-393984128, can't wait for that PR to get merged in...