Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TypeError: undefined is not an object when calling refetch from useQuery hook #6816

Closed
Tracked by #8596
Svarto opened this issue Aug 10, 2020 · 47 comments
Closed
Tracked by #8596
Assignees

Comments

@Svarto
Copy link

Svarto commented Aug 10, 2020

Intended outcome:
I have a react-native flatlist and I am dragging to refresh the current feed. I expected the original query to be refetch from the server and new data would be populated.

Same problem with fetchMore, I scroll down the flatlist and I expect query to add data to the list and continue scrolling through the additions.

Actual outcome:
I receive an error and the app crashes (both for refetch and fetchmore).

[Unhandled promise rejection: TypeError: undefined is not an object (evaluating '_this.currentObservable.refetch')]
- node_modules/@apollo/client/react/data/data.cjs.js:324:48 in _this.getQueryResult
- node_modules/regenerator-runtime/runtime.js:63:36 in tryCatch
- node_modules/regenerator-runtime/runtime.js:293:29 in invoke
- node_modules/regenerator-runtime/runtime.js:63:36 in tryCatch
- node_modules/regenerator-runtime/runtime.js:154:27 in invoke
- node_modules/regenerator-runtime/runtime.js:189:16 in PromiseImpl$argument_0
- node_modules/promise/setimmediate/core.js:45:6 in tryCallTwo
- node_modules/promise/setimmediate/core.js:200:22 in doResolve
- node_modules/promise/setimmediate/core.js:66:11 in Promise
- node_modules/regenerator-runtime/runtime.js:188:15 in callInvokeWithMethodAndArg
- node_modules/regenerator-runtime/runtime.js:211:38 in enqueue
- node_modules/regenerator-runtime/runtime.js:238:8 in exports.async
- node_modules/react-native/Libraries/Components/RefreshControl/RefreshControl.js:196:28 in _onRefresh
- node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-dev.js:286:4 in invokeGuardedCallbackImpl
- node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-dev.js:497:2 in invokeGuardedCallback
- node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-dev.js:521:2 in invokeGuardedCallbackAndCatchFirstError
- node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-dev.js:683:41 in executeDispatch
- node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-dev.js:707:19 in executeDispatchesInOrder
- node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-dev.js:872:28 in executeDispatchesAndRelease
- node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-dev.js:853:4 in forEachAccumulated
- node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-dev.js:897:20 in runEventsInBatch
- node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-dev.js:1069:18 in runExtractedPluginEventsInBatch
- node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-dev.js:2835:35 in batchedUpdates$argument_0
- node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-dev.js:20569:13 in batchedUpdates$1
- node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-dev.js:2731:29 in batchedUpdates
- node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-dev.js:2834:16 in _receiveRootNodeIDEvent
- node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-dev.js:2854:25 in receiveEvent
- node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js:425:19 in __callFunction
- node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js:112:6 in __guard$argument_0
- node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js:373:10 in __guard
- node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js:111:4 in callFunctionReturnFlushedQueue
* [native code]:null in callFunctionReturnFlushedQueue

How to reproduce the issue:
Flatlist code:

export default function DiscoverFeed({ navigation }) {
  const theme = useTheme();

  const { data, error, loading, refetch, fetchMore } = useQuery(
    GET_RECIPE_FEED,
    {
      variables: { offset: 0 },
    }
  );

  if (error) return <Text>There was an error, try and reload.</Text>;
  if (loading) return <Loader />;

  const renderItem = ({ item }) => {
    return (
      <View style={styles.cardItems}>
        <RecipeCard item={item} navigation={navigation} />
      </View>
    );
  };

  return (
    <SafeAreaView style={styles.safeContainer} edges={["right", "left"]}>
      <FlatList
        style={styles.flatContainer}
        data={data.recipe}
        removeClippedSubviews={true}
        renderItem={renderItem}
        refreshing={loading}
        onRefresh={() => {
          refetch();
        }}
        keyExtractor={(item) => item.id.toString()}
        onEndReachedThreshold={0.5}
        onEndReached={() => {
          // The fetchMore method is used to load new data and add it
          // to the original query we used to populate the list
          fetchMore({
            variables: {
              offset: data.recipe.length,
            },
          });
        }}
      />
    </SafeAreaView>
  );
}

typePolicy:

// Type policy to manage pagination and receiving new data
export const cache = new InMemoryCache({
  typePolicies: {
    Query: {
      fields: {
        recipe: {
          merge: (existing = [], incoming, { args }) => {
            // On initial load or when adding a recipe, offset is 0 and only take the incoming data to avoid duplication
            if (args.offset == 0) {
              return [...incoming];
            }
            // This is only for pagination
            return [...existing, ...incoming];
          },
        },
      },
    },
  },
});

Query:

export const GET_RECIPE_FEED = gql`
  query GetRecipeFeed($offset: Int) {
    recipe(order_by: { updated_at: desc }, limit: 5, offset: $offset)
      @connection(key: "recipe") {
      id
      title
      description
      images_json
      updated_at
      dishtype
      difficulty
      duration
      recipe_tags {
        tag {
          tag
        }
      }
    }
  }
`;

Versions
System:
OS: macOS 10.15.6
Binaries:
Node: 14.6.0 - /usr/local/bin/node
npm: 6.14.7 - /usr/local/bin/npm
Browsers:
Firefox: 79.0
Safari: 13.1.2
npmPackages:
@apollo/client: ^3.1.3 => 3.1.3
@apollo/link-context: ^2.0.0-beta.3 => 2.0.0-beta.3
apollo-cache-persist: ^0.1.1 => 0.1.1

@developmentnoblenexus
Copy link

Hi, recently noticed that we've the same error.

Using 3.0.2 again solved our problem.

@apollo/client: ^3.1.3 => ^3.0.2
Actual updates
next: ^9.5.2
next-apollo: ^5.0.0

@Svarto
Copy link
Author

Svarto commented Aug 17, 2020

@cc-dev-leader thanks for the tip! Will try and downgrade and see if it solves the problem. Very peculiar that it works with an older version...

@Poky85
Copy link

Poky85 commented Aug 24, 2020

I created the sandbox, but I'm not able to reproduce the problem reliably. In my application the problem occurs after some kind of component update (cannot reproduce in sandbox). What I figured out is that it always happens after React fast-refresh (try in sandbox).

https://codesandbox.io/s/apollo-client-3-nextfetchpolicy-bug-demo-ej8h5

@Svarto
Copy link
Author

Svarto commented Aug 27, 2020

@Poky85 This matches my use case too, only get it with fast refresh and not in production!

@wyqydsyq
Copy link

wyqydsyq commented Sep 2, 2020

I just ran into this upgrading from apollo-client/v2 to @apollo/client@3.1.4 and found this happening in any existing components that use refetch, downgrading back to 3.0.2 as @cc-dev-leader suggested seems to be an okay work-around so far.

@psamim
Copy link
Contributor

psamim commented Sep 15, 2020

Just installed version 3.2.0 and still having this problem on react-native.

@codelover2k
Copy link

it looks like its a known bug

a workaround that works with Next.js:

const { loading, error, data = {}, refetch: _refetch  } = useQuery(MY_QUERY);
const refetch = useCallback(() => { setTimeout(() => _refetch(), 0) }, [_refetch]);

@psamim
Copy link
Contributor

psamim commented Sep 16, 2020

@codelover2k Thanks. I tried your code but it does not work on react-native, is there any other way or a workaround to refetch the query with new variables or should I use another GraphQL client than Apollo?

@Svarto
Copy link
Author

Svarto commented Sep 16, 2020

@psamim, did you try disabling fast refresh and see if you still get the error?

@psamim
Copy link
Contributor

psamim commented Sep 16, 2020

@Svarto Thanks. Yes ti solved the problem.

@codelover2k
Copy link

this also works for me:

const { loading, error, data = {}, refetch: _refetch  } = useQuery(MY_QUERY);
const refetch = (args) => _refetch(args);

make sure to restart your Next.js server

the only problem that I have now, it that refetch fetches the correct data, but it does not re-render 😞

@aakagi
Copy link

aakagi commented Sep 22, 2020

I'm using @apollo/client v3.2.0 + Expo v39 and I'm able to use useLazyQuery as a workaround!

const [fetchData, { data, refetch, loading }] = useLazyQuery(GQL_QUERY, { variables });

React.useEffect(() => {
  fetchData();
}, []);

...

return (
  <FlatList
    ...
    onRefresh={refetch} // this works now!
  />
);

@brandontle
Copy link

Still running into this issue in production with:

@apollo/client: 3.0.2
next": ^9.5.2

@codelover2k's suggestions work, but obviously isn't a great long-term solution. Any other suggestions?

@alexmngn
Copy link

alexmngn commented Sep 24, 2020

I'm seeing a similar issue when I try to call refetch on an unmounted component.
In my case, I need to make that refetch query after a certain setTimeout and by the time the timeout is triggered, the user might have navigated away and the component is unmounted. Though, I still need that query to be triggered.

  const { data, refetch } = useQuery(QUERY);

  const onSuccess = () => {
    setTimeout(() => {
      refetch(); // this crashes. 
    }, 10000);
  }

  // called at some point
  onSuccess();

Screenshot 2020-09-24 at 15 32 34

@petrbela
Copy link

petrbela commented Sep 24, 2020

@alexmngn I think in your case, the hook would be rightly unmounted. You might need to retain a reference to the function:

const onSuccess = () => {
  const _refetch = refetch;
  setTimeout(() => {
    _refetch();
  }, 10000);
}

Though I'm not sure this will work. Alternatively, you might try processing the callback in a redux store or similar or call the query directly on the Apollo Client. client.query({ ... })

@Svarto
Copy link
Author

Svarto commented Sep 28, 2020

I am still getting this intermittently, it looks like in some cases the useQuery hook does not get called if you navigate back to the page - at that point of course these two functions are undefined. It happens even with the refetch function wrapped in a useCallback:

  const refetch = useCallback(() => {
    setTimeout(() => _refetch(), 10);
  }, [_refetch]);

Would be great to get a fix as it happens in production too where a user can't refresh or fetchMore list items and everything is just stuck...

@spsaucier-bakkt
Copy link

This appears to be a continuation of the issue here: apollographql/react-apollo#3600

@morgangallant
Copy link

I'm using @apollo/client v3.2.0 + Expo v39 and I'm able to use useLazyQuery as a workaround!

const [fetchData, { data, refetch, loading }] = useLazyQuery(GQL_QUERY, { variables });

React.useEffect(() => {
  fetchData();
}, []);

...

return (
  <FlatList
    ...
    onRefresh={refetch} // this works now!
  />
);

@aakagi's solution worked well for me on @apollo/client v3.1.4, swapped out all useQuery with useLazyQuery and did the initial fetch in a useEffect, just as shown above. No more fast reload errors. Thanks for all the help!

@LauraBeatris
Copy link

LauraBeatris commented Oct 17, 2020

Any updates on this? The useQuery should work as expected... We're having a lot of problems to implement stuff such as fast-refresh.

I have tried to use lazy queries as a workaround but it didn't work with the fetch policy set as cache-and-network #7171

By dealing with this error, we're being forced to use lazy queries where it isn't necessary at all

@linmic
Copy link
Contributor

linmic commented Oct 31, 2020

@LauraBeatris Could you try 3.3.0-beta.16 to see if this issue goes away?

@sajadghawami
Copy link

@linmic having the same problem, but upgrading to 3.3.0-beta.16 didn't help

@mribichich
Copy link

I can confirm that 3.3.0-rc.0 fixed the problem

@louisholley
Copy link

@mribichich still happening to me in 3.3.0-rc.0

@ngschaider
Copy link

Upgrading from 3.2.5 to 3.3.0-rc.0 fixed this issue for me.

@rossenv
Copy link

rossenv commented Nov 19, 2020

Upgrade to 3.2.7 solved the problem in my case

@AdityaSamanta
Copy link

Upgrade to 3.2.7 solved the problem in my case

This resolved the issue for me - thanks

@steven-ossorio
Copy link

steven-ossorio commented Nov 25, 2020

Upgrading to 3.2.7 did not solve this issue in my case. Whenever I save my code and it performs a fast-refresh I continue to see the same error but for fetchMore. Has anyone found a workaround? Thank you for sharing.

@dngconsulting
Copy link

3.2.9 did not solve the issue here... How is it possible to keep such a bug in the backlog ? We are talking here about a simple refetch() ...

@PundirKajal
Copy link

Upgrading to 3.3.0-rc.0 fixed this issue for me.

@deathemperor
Copy link

I can confirm 3.3.7 fixed this.

@juanstiza
Copy link

We are on 3.3.7 and we are seeing the same behavior when using fetchMore. We also tried useLazyQuery and didn't work.

@ayepRahman
Copy link

Same issue as @juanstiza , on 3.3.7 facing the same issue using fetchMore with useLazyQuery

@juanstiza
Copy link

I did test it on a Snack though, and it works correctly. Also, to add a bit more information, I wrapped fetchMore like this:

const fetchMore = useCallback((args) => {
    if (_fetchMore) {
      try {
        _fetchMore(args);
      } catch (e) {
        console.log(`Invocation error: ${e.message}`);
      }
    } else {
      console.warn(`Fetch more is not available`);
    }
  }, [_fetchMore]);

The functionality is there, new items are fetched, and it works correctly. But the errors are still being thrown.

@juanstiza
Copy link

Ok, small update. After creating a new Expo project without being able to reproduce the error, I can tell now that the culprit is Storybook... wonder if others have had the same issue with it.
The solution so far is to not use storybook when running this type of operations.

@petrbela
Copy link

petrbela commented Feb 2, 2021

@juanstiza I'm seeing this issue outside of Storybook, too. And checking for if (_fetchMore) is a workaround to fix for _fetchMore being undefined, which still continues to be an issue, and I wouldn't say that "it works correctly" :) It's a valid temporary fix but the underlying problem still stands.

@ayepRahman
Copy link

I made a small change by safe checking fetchMore(). Work as expected and no error shown.
I'am at 3.3.7

fetchMore && fetchMore(....)

@Dakuan
Copy link

Dakuan commented Feb 20, 2021

Im on 3.3.7 and still have this isssue, @ayepRahman approach hasn't helped either

@Dakuan
Copy link

Dakuan commented Feb 20, 2021

I found a fix for my issue:

import React from "react";
import { InteractionManager } from "react-native";

export const useIsPageTransitioning = (): boolean => {
  const [isTransitioning, setIsTransitioning] = React.useState(true);
  React.useEffect(() => {
    const h = InteractionManager.runAfterInteractions(() => {
      setIsTransitioning(false);
    });
  }, []);

  return isTransitioning;
};

change to:

import React from "react";
import { InteractionManager } from "react-native";

export const useIsPageTransitioning = (): boolean => {
  const [isTransitioning, setIsTransitioning] = React.useState(true);
  React.useEffect(() => {
    const h = InteractionManager.runAfterInteractions(() => {
      setIsTransitioning(false);
    });
    return () => h.cancel();
  }, []);

  return isTransitioning;
};

@kevinnguy
Copy link

I had to patch @apollo/client with patch-package to get rid of this issue: https://gist.github.com/kevinnguy/ea3d1f92f078cae86d1f7b936fea06af

@emmaadesile
Copy link

After trying several suggestions including useLazyQuery with no success, I upgraded @apollo/clientto 3.3.12 and that fixed it for me

@tabvn
Copy link

tabvn commented May 5, 2021

simulator_screenshot_5C873C71-28F5-4954-B8CB-9C0B75E2EC80

"@apollo/client": "^3.3.16",

@d4nielhdz
Copy link

This is also happening to me when calling fetchMore in a scroll event. I'm on the ^3.3.16 version.

@Rhomennik
Copy link

+1

@nghiatv
Copy link

nghiatv commented Jul 20, 2021

any update?

@brainkim brainkim mentioned this issue Aug 13, 2021
14 tasks
@zanemcca
Copy link

+1

@dancon
Copy link

dancon commented Nov 18, 2021

The problem is still exist in 3.4.16

@brainkim
Copy link
Contributor

Should be fixed in 3.5!

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Feb 15, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests