Search code examples
react-nativeapollo-clientreact-apollographql-subscriptionsapollo-cache-inmemory

Apollo cache query fields policy offsetLimitPagination() doesnt work with subscriptions


I use apollo client for react native.

When I use offsetLimitPagination() for pagination my subscriptions doesn't update cache.

Subscriptions works correctly but doesn't update flatlist data. When i remove offsetLimitPagination function it works. I can't use together subscriptions and offsetLimitPagination function on cache.

Is there any solution for that?`

Thanks.

Cache

const cache = new InMemoryCache({
    typePolicies: {
       Query: {
          fields: {
             chatDetail: offsetLimitPagination(),
        }
    }
},
});

ChatDetailPage

import React, { useState, useCallback } from 'react'
import { StyleSheet, Text, View, FlatList } from 'react-native'
import { ActivityIndicator } from 'react-native-paper';


import { useQuery } from '@apollo/client'
import { useSelector } from 'react-redux'

import { CHAT_DETAIL } from '../../../Graphql/Queries/Message'
import { MESSAGE_SUB } from '../../../Graphql/Subscriptions/Message'

import MainFlow from './Components/Flow/MainFlow'


const ChatDetailMain = () => {
     const user = useSelector(state => state.auth.user)
     const currentRoom = useSelector(state => state.room.currentRoom)
     const [hasNext, setHasNext] = useState(true)
     const limit = 15
     const { error, loading, data, refetch, fetchMore, subscribeToMore } = useQuery(CHAT_DETAIL, { 
     variables: { userId: user._id, roomId: currentRoom._id, limit }, fetchPolicy: "cache-and- 
network", 
nextFetchPolicy: "cache-first" })

 // render item
 const renderItem = (
    ({item} ) => {
        return <MainFlow item={item} />
    }
)
if (error) {
    console.warn('CHAT_DETAIL QUERY ERROR: ', error)
    console.log(error.message);
    return (
        <View>
            <Text>
                An Error Occured: {error.message}
            </Text>
        </View>
    )
}
if (loading || data == undefined || data == null) {
    return (
        <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
            <ActivityIndicator size={50} color="gray" />
        </View>
    )
}
// fetchMore
const fetchMoreData=()=>{
    // console.log("fetchMore runnig hasnext limit, data.chatDetail.length >= limit ", hasNext, limit, data.chatDetail.length >= limit);
    if(hasNext && data.chatDetail.length >= limit){
        fetchMore({
            variables:{
                offset: data.chatDetail.length,
                limit: data.chatDetail.length+limit
            }
        }).then((flowMoredata)=>{
            if(flowMoredata.data.chatDetail.length==0 || flowMoredata.data.chatDetail.length === data.chatDetail.length){
                setHasNext(false)
            }
        })
    }
}
// subscription area
const subscribeQ = () => subscribeToMore({
    document: MESSAGE_SUB,
    variables: {
        userId: user._id
    },
    updateQuery: (prev, { subscriptionData }) => {
        if (!subscriptionData.data) return prev;
        const { messageSub } = subscriptionData.data
        let current = {}
        let others = []
        switch(messageSub.type){
            case 'update':
                prev.chatDetail.map(message => {
                    if (message._id != messageSub.message._id) others.push(message)
                    if (message._id == messageSub.message._id) current = messageSub.message
                })
                return { chatDetail: [ current, ...others ]}
            case 'remove':
                prev.chatDetail.map(message => {
                    if (message._id != messageSub.message._id) others.push(message)
                    if (message._id == messageSub.message._id) current = messageSub.message
                })
                return { chatDetail: [ ...others ]}
            case 'create':
                return { chatDetail: {  ...prev, chatDetail: [ messageSub.message, ...prev.chatDetail] }}

            default: return { ...prev }
            }
    }
})
if (subscribeToMore != undefined && subscribeToMore) {
    subscribeQ()
}
return (
    <View>
        <FlatList
            data={data.chatDetail}
            renderItem={renderItem}
            keyExtractor={(item, index) => String(index)}
            onEndReached={fetchMoreData}
            onEndReachedThreshold={0.2}
            contentContainerStyle={{ paddingTop: 80 }}
            inverted={true}
        />
    </View>
)
 }

  export default ChatDetailMain

  const styles = StyleSheet.create({})

Solution

  • It was about cache merge issue. If you want to cache data, you shoul give a key to apollo client "Cache according to the what, for each room has an id or roomName for keyArgs param it should uniqe value like that

    const cache = new InMemoryCache({
    typePolicies: {
       Query: {
          fields: {
             
             chatDetail: {
                    keyArgs:['roomId'],
                    merge(incoming=[], existing=[]){
                   
                   .
                   .
                   .
                   return offsetLimitPagination()
           }}
        }
      }
     },
    });