Search code examples
react-nativegraphqlapolloreact-apolloapollo-server

When state changes for graphql variable, result stays the same on react-native


I'm trying to create an app using shopify graphql api to create an ecommerce app on react native expo.

I have an onPress that calls a setState to change the state of the graphQL variable but the results don't change from the initial state of 'currentSubCategories'


   const [currentSubCategories, setSubCategories] = useState(Categories[0].subCategory[0].handle);
    
      let {
        collection,
        loading,
        hasMore,
        refetch,
        isFetchingMore,
      } = useCollectionQuery(currentSubCategories, first, priceRange);

    const [currentCategory, setCategory] = useState({categories: Categories[0]});


    const onSubCategorySelect = (subCategory) => { setSubCategories(subCategory.handle) }

    onPress={() => onSubCategorySelect(item)}

    function useCollectionQuery(
      collectionHandle: string,
      first: number,
      priceRange: [number, number],
    ) {
      let [isInitFetching, setInitFetching] = useState<boolean>(true);
      let [isReloading, setIsReloading] = useState<boolean>(true);
      let [collection, setCollection] = useState<Array<Product>>([]);
      let isFetchingMore = useRef<boolean>(false);
      let hasMore = useRef<boolean>(true);
    
      let defaultCurrency = useDefaultCurrency().data;
    
      let { data, loading, refetch: refetchQuery } = useQuery<
        GetCollection,
        GetCollectionVariables
      >(GET_COLLECTION, {
        variables: {
          collectionHandle,
          first,
          sortKey: ProductCollectionSortKeys.BEST_SELLING,
          presentmentCurrencies: [defaultCurrency],
        },
        notifyOnNetworkStatusChange: true,
        fetchPolicy: 'no-cache',
      });
    
      let getMoreUntilTarget = async (
        targetAmount: number,
        cursor: string | null,
        handle: string,
        filter: [number, number],
      ) => {
        let result: Array<Product> = [];
        let moreData: Array<Product> = [];
    
        let { data } = await refetchQuery({
          first,
          collectionHandle: handle,
          after: cursor,
        });
    
        ...
    
      useEffect(() => {
        if (!loading) {
          isFetchingMore.current = false;
        }
        if (isInitFetching && !!data && !!data.collectionByHandle) {
          let newCollection = mapToProducts(data.collectionByHandle.products);
          hasMore.current = !!data.collectionByHandle?.products.pageInfo
            .hasNextPage;
          setCollection(newCollection);
          setIsReloading(false);
          setInitFetching(false);
        }
      }, [loading, isInitFetching]); // eslint-disable-line react-hooks/exhaustive-deps
    
      return {
        collection,
        loading: isReloading,
        hasMore: hasMore.current,
        isFetchingMore: isFetchingMore.current,
        refetch,
      };
    }

I'm using flatList to show the result

    <FlatList
        data={collection}
        renderItem={({ item }) => (
           <Text>{item.title}</Text>
        )}
    />

Solution

  • According to docs you have to pass new variables to refetch otherwise refetch will use initial values.

    In this case (custom hook) you have 2 ways to solvethis problem:

    • return variables from your custom hook (taken from useQuery),
    • return some own refetch function.

    1st option needs 'manual' variables updating like:

    refetch( { ...variablesFromHook, collectionHandle: currentSubCategories } );
    

    In 2nd case you can create myRefetch (and return as refetch) taking collectionHandle parameter to call refetch with updated variables - hiding 'complexity' inside your hook.

    Both cases needs refetch call after updating state (setSubCategories) so you should use this refetch inside useEffect with [currentSubCategories] dependency ... or simply don't use state, call refetch directly from event handler (in onSubCategorySelect).