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 stuck on true when using useQuery hook? #3270

Closed MaxwellGover closed 5 years ago

MaxwellGover commented 5 years ago

I forked the hooks example from the react-apollo repo and it seems like the value for loading is never being updated, even when the data is being returned properly?

Screen Shot 2019-07-22 at 4 43 39 PM

I set up my own project and was experiencing the same issue when using useQuery

UPDATE: After adding an item, useQuery seems to work fine

Screen Shot 2019-07-22 at 4 52 19 PM

Intended outcome:

value of loading changes to false when data is available

Actual outcome:

value of loading never updates to false

Version

{
  "name": "client",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@apollo/react-hooks": "beta",
    "@types/jest": "24.0.13",
    "@types/node": "12.6.8",
    "@types/react": "16.8.23",
    "@types/react-dom": "16.8.4",
    "apollo-cache-inmemory": "^1.6.0",
    "apollo-client": "^2.6.3",
    "apollo-link-http": "^1.5.14",
    "apollo-link-ws": "^1.0.17",
    "graphql": "^14.3.1",
    "graphql-tag": "^2.10.1",
    "react": "16.8.6",
    "react-dom": "16.8.6",
    "react-scripts": "3.0.1",
    "reactstrap": "^8.0.0",
    "subscriptions-transport-ws": "^0.9.16",
    "typescript": "3.5.3"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  },
  "devDependencies": {
    "@types/graphql": "14.2.2",
    "@types/reactstrap": "8.0.1"
  }
}
gilesbradshaw commented 5 years ago

I've been finding that sometimes I don't seem to get the second call of the component even though the data has loaded - I am currently triggering a state change with the onCompleted call back

  const [qRefresh, setQRefresh] = useState(0)

  const {
    data: d,
    loading,
    error,
    client: {
      store
    }
  } = useQuery<parameter, parameterVariables>(
    query,
    {
      onCompleted: (...params) => {
        setQRefresh(qRefresh + 1)
      },
      fetchPolicy: 'network-only',
      variables: {
        id,
      },
    },
  )
Grmiade commented 5 years ago

@MaxwellGover FYI the previous version (@apollo/react-hooks 0.1.0-beta.10) seems to work for me. I'm not longer stuck by the loading state. You should try this version on your project I think, maybe it could be a good clue to debug this issue ;)

gilesbradshaw commented 5 years ago

@MaxwellGover i just tried beta 10 and fixes my issue..

gilesbradshaw commented 5 years ago

Also my components continually refresh when using beta 11 whereas when I change to beta 10 this stops happening - I suspect this is also related to the browser eventually crashing. beta 11 is unwell IMO

hmeerlo commented 5 years ago

I'm using react-apollo 3.0.0-beta4 which uses 0.1.0-beta.11 and I have the same issue. The loading state stays true and I can see in the network inspector that the data has arrived, it just has never re-rendered. I suspect it has something to do with the fact that I'm using 2 hook queries in 1 component?

MichaelHindley commented 5 years ago

@hmeerlo I don't think so, I am also using beta.11 and I have this issue with a simple 1 query component:

const BOOKS_GQL = gql`
    query GetBooks {
        books {
            id,
            author,
            title
        }
    }
`
const BookList = () => {
  const { loading, data } = useQuery<GetBooks>(BOOKS_GQL)
  if (loading) return <p>Loading...</p>
  if (!data) return <></>

  return (
    <div>
      {data.books.map((book, idx) => {
        return <p key={idx}>{book.title} by {book.author}</p>
      })}
    </div>
  )
}

const App = () => (
  <ApolloProvider client={client}>
    <div>
      <h2>Typehint all the things and eat delicious chicken wings</h2>
      <BookList />
    </div>
  </ApolloProvider>
)
viniciusdacal commented 5 years ago

I'm using 3.0.0-beta.4 as well, and I'm facing the same issue.

viniciusdacal commented 5 years ago

@hwillson any clue about this issue? If you give a direction, I could try to open a PR. I tried to investigate, but I couldn't grok QueryData entirely.

MichaelHindley commented 5 years ago

After some debugging locally, at least for me it only occurs when the result of the Query has 1 item. If the result of the Query has more than 1 item, loading does not get stuck in a false state.

hmeerlo commented 5 years ago

@MichaelHindley not for me, the result of the query is the same all the time, but it randomly exits the loading state. Maybe it's timing related or just random :-)

Bedotech commented 5 years ago

I notice something really weird, if I set in the network panel some network throttling, for example, Fast 3G, everything work as excepted.

This implementation is not affected: https://github.com/trojanowski/react-apollo-hooks

emilsjolander commented 5 years ago

@Bedotech I think you are on to something. I just encountered this bug in our product after implementing caching on the server which means queries are returned a lot faster. If i introduce a slowing using a sleep on the server everything works as expected. Seems like some sort of race condition to me.

viniciusdacal commented 5 years ago

There's definitely the race condition. If I got it right, we are storing the whole state inside a ref object (useRef), Changes to this object, doesn't trigger an update inside the component. Which means, it could happen that loading is set to false, but the component doesn't react (re-render) to that change.

What happens sometimes, is that something else change in the component or in the tree, triggering a re-render and removing it from the stuck state.

Ideally, we should store data, loading, error, variables and so on using useState or useReducer

pdemarino commented 5 years ago

This seems to be manifesting itself in #3009 - the client-only data resolves immediately, triggering the race condition.

hwillson commented 5 years ago

Hi all - I'm just getting back from vacation, and am slowly re-learning how to type. I'll digest this more thoroughly shortly.

viniciusdacal commented 5 years ago

Thank you @hwillson If there's anything I can do to help, let me know.

therahl commented 5 years ago

This is an issue for me as well. Currently getting around it by forcing multiple rerenders via state change from the onCompleted prop - it's ugly but working as a hack for now.

thomas81528262 commented 5 years ago

onCompleted seems like a solution now...... any update?

ehortle commented 5 years ago

I was also experiencing this issue on "react-apollo": "^3.0.0-beta.4" but switching to "@apollo/react-hooks": "0.1.0-beta.10" as per @gilesbradshaw 's comment fixed my issue. Thanks, @gilesbradshaw !

thomas81528262 commented 5 years ago

Should we roll back to 0.1.0-beta.10?

hwillson commented 5 years ago

Does anyone here have a small runnable repro that shows this happening? This is on my radar to fix today, and a runnable repro would greatly help.

hwillson commented 5 years ago

@MaxwellGover I can't seem to re-create this issue using a fork of the hooks example app. If it's still happening for you consistently, could you share your fork so I could try it out directly?

@Bedotech The internals of react-apollo-hooks are quite a bit different than this project, so I'm not surprised to hear it's working with react-apollo-hooks. There's a bug here somewhere for sure.

@viniciusdacal our use of useRef should be okay here. We're leveraging useRef to abstract a lot of the internals out of the hook itself (for future reasons), but lifecycle updates should still be reflected properly back in the wrapping component. If useRef was to blame here we would see quite a few other failures in our test suite. But, bugs will be bugs, so I'll definitely keep your suggestion in mind while debugging.

hwillson commented 5 years ago

Hmm - https://github.com/apollographql/react-apollo/issues/3299 definitely sounds related. I'll run with that repro for now.

jBugman commented 5 years ago

This also reproduces for me on beta11 (but not 10) with @client query (and custom resolver for this query if this helps).

hwillson commented 5 years ago

I've found the issue; it's a race condition between the response coming back through the internal Apollo Client ObservableQuery subscription and the component being marked as mounted. When a response comes back from AC via the Observable, we manually trigger a component update. That update is blocked if the component is marked as not being mounted. The component is marked as being mounted via a useEffect call, but sometimes data comes back via the Observable before the useEffect has a chance to fire. I'm just nailing down some tests for this, but a PR is coming shortly. Thanks all!

hwillson commented 5 years ago

https://github.com/apollographql/react-apollo/pull/3313 should have this fixed. I've published new beta's if anyone here is able to test things out?

dmarkow commented 5 years ago

@hwillson This seems to have fixed it for me 👍

jBugman commented 5 years ago

Update to .12 fixed my issue too

thomas81528262 commented 5 years ago

update to .12 fixed issue!!!!!!!!!!

simmo commented 5 years ago

.12 is also fixing it for me.

nicoglennon commented 5 years ago

Thanks for the quick fix and the awesome library @hwillson!

cthurston commented 5 years ago

I'm still getting this on react-apollo@3.0.1, @apollo/react-hooks@3.0.1.

You can inspect the network and see that the request has completed with data, but it still will say loading.

It works sometimes. When I first began this morning is successfully set loading=false, but on the second load it didn't (even on hard refresh). Then every 30 minutes or so it will set loading=false. Maybe related to development/hot-reloading.

temm1210 commented 5 years ago

well i faced this issue on @apollo/react-hooks:0.1.0-beta.10. but in my case it is no longer visible on @apollo/react-hooks@3.0.1 until now

ehortle commented 5 years ago

Contrary to my above comment, I am still getting this issue. However, only in our live deployment, not in local dev... No idea where to look though. Would be happy to try to generate a reproduction but not sure where to start if it's only in live! Open to ideas if anybody has any...

petetnt commented 5 years ago

Having the same issue with both @3.0.1 and the @3.1.0-beta.0 in development. The query resolves correctly, but the data doesn't get filled and loading is stuck to true.

Related deps:

    "@apollo/react-hooks": "3.1.0-beta.0",
    "apollo-cache": "^1.3.2",
    "apollo-cache-inmemory": "^1.2.1",
    "apollo-client": "^2.6.4",
    "apollo-link-context": "^1.0.5",
    "apollo-link-error": "^1.0.5",
    "apollo-link-http": "^1.3.3",
    "apollo-link-rest": "^0.3.1",
    "apollo-link-schema": "^1.1.0",
    "apollo-server-express": "^2.1.0",
    "apollo-upload-client": "^9.1.0",
    "apollo-utilities": "^1.3.2",
    "react-apollo": "3.1.0-beta.0",
    "graphql": "14.3.1",
    "graphql-anywhere": "^4.1.22",
    "graphql-cli": "2.15.14",
    "graphql-tag": "^2.8.0",
    "graphql-tools": "4.0.5"

Query is just a simple query:

const GET_DATA = gql`
  query GetData(
    $ID: String!,
  ) {
    GetDataFoo(
      ID: $ID,
    ) {
      data {
        time
        foo
      }
    }
  }
`;

...
  const x = useQuery(GET_DATA, {
    variables: {
      id
    },
  });
diazdell91 commented 5 years ago

some new solution? @hwillson

armanrozika commented 5 years ago

Face same issue here: "@apollo/react-hooks": "^3.1.0", "apollo-cache-inmemory": "^1.6.3", "apollo-client": "^2.6.4", "apollo-link-http": "^1.5.16"

Been searching online for hours for this now, and I just did a hacky way to solve it, by using errorPolicy: "ignore". It can't read the graphql error (or it can?), but it returns data.yourData as false, so I use that instead to show error on the UI, ended up hardcode the error message from frontend tho.

Colafornia commented 5 years ago

Contrary to my above comment, I am still getting this issue. However, only in our live deployment, not in local dev... No idea where to look though. Would be happy to try to generate a reproduction but not sure where to start if it's only in live! Open to ideas if anybody has any...

Same problem, although I have upgraded to: "@apollo/react-hooks": "^3.1.2",

AlvinRapada commented 5 years ago

still a problem with "@apollo/react-hooks": "^3.1.2"

Adzz commented 4 years ago

I m getting the same problem locally. I can see the query has returned results in the chrome network tab:

Screenshot 2019-11-10 at 19 17 08

More over if I do this:


const link = new HttpLink({ uri: "https://localhost:4001/api" });
const cache = new InMemoryCache();
const client = new ApolloClient({ cache, link });
client
  .query({
    query: gql`
      {
        property {
          images {
            url
          }
        }
      }
    `,
  })
  .then((result) => console.log(result));

It also console logs what I would expect. However in the component, when using a hook:

  const { loading, error, data } = useQuery(IMAGE_QUERY, {
    notifyOnNetworkStatusChange: true,
  });

  console.log(data);
  console.log(error);
  console.log(loading);

Data is always undefined as is the error object, network status of 1. I would copy you the whole result of the useQuery but that seems impossible (can't stringify it as it's circular...)

br-dev commented 4 years ago

I seem to have this same issue with @apollo/client 3.0.0-beta.7. I'm running on react-native (expo) while connected to the React Native Debugger. If I disconnect from the debugger, I don't seem to have this issue. The version of React Native Debugger I'm using is 0.10.2. Perhaps this issue is with RND?

AlbertMontolio commented 4 years ago

I m getting the same problem locally. I can see the query has returned results in the chrome network tab:

Screenshot 2019-11-10 at 19 17 08

More over if I do this:

const link = new HttpLink({ uri: "https://localhost:4001/api" });
const cache = new InMemoryCache();
const client = new ApolloClient({ cache, link });
client
  .query({
    query: gql`
      {
        property {
          images {
            url
          }
        }
      }
    `,
  })
  .then((result) => console.log(result));

It also console logs what I would expect. However in the component, when using a hook:

  const { loading, error, data } = useQuery(IMAGE_QUERY, {
    notifyOnNetworkStatusChange: true,
  });

  console.log(data);
  console.log(error);
  console.log(loading);

Data is always undefined as is the error object, network status of 1. I would copy you the whole result of the useQuery but that seems impossible (can't stringify it as it's circular...)

I have exactly the same issue

hamzakubba-opendoor commented 4 years ago

I found a workaround in 3.1.3, which is to set { pollInterval: 0 } as an option. Not sure why it fixes it, but seems to do so consistently for me.

hyochan commented 4 years ago

I am not sure either but when I've removed split, it started working.

// const link = split(
//   // split based on operation type
//   ({ query }) => {
//     const definition = getMainDefinition(query);
//     return (
//       definition.kind === 'OperationDefinition' &&
//       definition.operation === 'subscription'
//     );
//   },
//   // wsLink,
//   httpAuthLink,
// );

export default new ApolloClient({
  link: authLink.concat(httpLink),
  cache,
  resolvers,
});
wintercounter commented 4 years ago

Same issue here. None of the above hacks seem to work for me. We tried refetch, forced rerender after a few seconds, not working. However setting notifyOnNetworkStatusChange: true made it work. Only Safari is affected for some reason.

bulutfatih commented 4 years ago

For a temporary solution, you can try to set fetchPolicy: 'no-cache'

jesuslopezlugo commented 4 years ago

I had the same issue on @apollo/react-hooks 3.1.3, this is my code with my workaround:

import React from "react";
import {FormattedMessage} from 'react-intl';
import {loader} from 'graphql.macro';
import {useQuery} from '@apollo/react-hooks';

const Workspace = () => {
    const GET_WORKSPACE = loader('./../../../graphql/Workspace/get_workspace.graphql');
    const {loading, error, data, refetch} = useQuery(
        GET_WORKSPACE,
        {
            variables: {user_id: "12345"},
            onCompleted: data => { }
        }
    );
    if (loading) return 'Loading...';
    if (error) return `Error! ${error.message}`;
    return (
        <div style={{padding: 24, background: '#fff', minHeight: 360}}>
            <h2><FormattedMessage id="dashboard.tasks.title" defaultMessage="Tasks"/></h2>
            {data.workspace.map(item => (
                <span key={item.id}> {item.name}</span>
            ))}
            <button onClick={() => refetch()}>Refetch!</button>
        </div>
    );
};

I found a solution of my infinite loop/loading stuck issue, just passing an empty function to onCompleted property of useQuery and problem solved 😕

I tried with fetchPolicy: 'no-cache' and pollInterval: 0 without sucess

lxe commented 4 years ago

I'm seeing this at a very intermittent fashion. Happened on its own in production, then disappeared, then happened again. I'm guessing it's dependent on the response data/timing/headers, etc...?

What I was seeing is that both loading and data are set, and refetch policy was not honored, and it was rerunning the query/updating react state every n seconds.

I don't know if I'll be able to reproduce this.

benjamn commented 4 years ago

I believe https://github.com/apollographql/apollo-client/pull/6120, just released in @apollo/client@3.0.0-beta.43, may help with some of these problems.

francisfueconcillo commented 4 years ago

Got stuck for this in a while, but figured out watching for the data instead of the loading flag works for me.