微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

添加或删除带有 apollo 缓存和反应变量的项目后 UI 不更新

如何解决添加或删除带有 apollo 缓存和反应变量的项目后 UI 不更新

在使用 apollo 缓存的 react native 应用上添加删除项目后,我很难更新我的 UI。>

再解释一下。我有一个 explorer Screen 其中显示了一些带有切换订阅取消订阅的项目。在另一个名为“订阅屏幕”的屏幕上,我应该显示所有我最喜欢的项目。因此,我创建了一个名为 allFavoritesVar 的反应性变量,我可以在其中添加删除项目。

所以在我的 cache.js我有

import {InMemoryCache} from '@apollo/client/core';
import {makeVar} from '@apollo/client';

export const allFavoritesVar = makeVar([]);

export const cache = new InMemoryCache({
  Query: {
    fields: {
      userFavorites: {
          read() {
            return allFavoritesVar();
          },},}
  }
})

因此,在我的资源管理器屏幕上,我正在检查 allFavoritesVar 中是否存在每个项目,以将切换开关设为红色并通知用户这些项目已在他们的“订阅屏幕”中。

const favExists = (flux) => {
    if (allFavoritesVar().filter((item) => item.id === flux.id).length > 0) {
      return true;
    }
    return false;
  }; 

之前使用的是 redux 并切换到 apollo,因为我需要在用户打开他们的应用程序时保留缓存。使用 redux,一切都变得更简单了,在从商店中添加删除项目时,切换工作正常并变成红色或灰色,并且“订阅屏幕”正在自我更新。

现在,当我切换时,突变起作用了,我可以看到添加删除了项目,但我的 ui 没有更新。当我关闭我的应用程序时,不会显示缓存的最后状态。

这是我的浏览器屏幕

import React,{useEffect,useState} from 'react';
import {
  SafeAreaView,StyleSheet,Dimensions,ScrollView,TouchableOpacity,Image,FlatList,ActivityIndicator,} from 'react-native';
import {
  NetworkStatus,useLazyQuery,useMutation,useQuery,} from '@apollo/client';
import {useSelector,usedispatch} from 'react-redux';
import {Box,Text} from 'react-native-design-utility';
import {Notifier} from 'react-native-notifier';
import {useTheme} from '@react-navigation/native';
import ErrorIcon from 'react-native-vector-icons/Ionicons';
import RefreshIcon from 'react-native-vector-icons/Ionicons';
import {theme} from '../theme/theme';
import Loading from '../components/Loading';
import CustomNotifier from '../components/CustomNotifier';
import CustomNotifierError from '../components/CustomNotifierError';
import SubscribeItem from '../components/SubscribeItem';
import {
  SUBSCRIBE_FLUXGROUP_MUTATION,SUBSCRIBE_FLUX_MUTATION,UNSUBSCRIBE_FLUXGROUP_MUTATION,UNSUBSCRIBE_FLUX_MUTATION,} from '../graphql/mutations/fluxMutations';
import {
  GET_EXPLORER_CATEGORIES_QUERY,GET_EXPLORER_SLIDES_QUERY,} from '../graphql/queries/explorerQueries';
import ToggleIcon from '../components/ToggleIcon';
import {HEIGHT} from '../utils/constants';
import {ALL_FAVORITES_QUERY} from '../graphql/queries/userQueries';
import {allFavoritesVar,cache} from '../utils/cache';
import {FLUX_QUERY} from '../graphql/queries/fluxesQueries';

const WIDTH = Dimensions.get('window').width;
const PAGE_SIZE = 10;

const ExplorerScreen = ({navigation}) => {
  const {colors,dark} = useTheme();
  const [limit,setLimit] = useState(PAGE_SIZE);
  const [isError,setError] = useState('');
  const [isLoading,setIsLoading] = useState(false);
  const {
    data: explorerData,loading: explorerLoading,error,refetch,} = useQuery(GET_EXPLORER_CATEGORIES_QUERY,{
    fetchPolicy: 'cache-first',errorPolicy: 'all',});
  const {data: favoritesData,loading: favLoading} =
    useQuery(ALL_FAVORITES_QUERY);
  const {data: slidesData,loading: slidesLoading} = useQuery(
    GET_EXPLORER_SLIDES_QUERY,{
      fetchPolicy: 'cache-first',);
  const [subscribetoFlux] = useMutation(SUBSCRIBE_FLUX_MUTATION);
  const [subscribetoFluxGroup] = useMutation(SUBSCRIBE_FLUXGROUP_MUTATION);
  const [unsubscribeFromFlux] = useMutation(UNSUBSCRIBE_FLUX_MUTATION);
  const [unsubscribeFromFluxGroup] = useMutation(
    UNSUBSCRIBE_FLUXGROUP_MUTATION,);

  const addFav = (flux) => {
    const explorerFav = allFavoritesVar([...allFavoritesVar(),flux]);
    console.log('explorerFav: ',explorerFav);
    return explorerFav;
  };

  const favExists = (flux) => {
    if (allFavoritesVar().filter((item) => item.id === flux.id).length > 0) {
      return true;
    }
    return false;
  };

  const handleAddFavorite = async (flux) => {
    if (flux.__typename === 'FluxGroup') {
      addFav(flux);
      Notifier.showNotification({
        title: 'Vous êtes abonné à ce groupe de flux',Component: CustomNotifier,componentProps: {
          alertType: 'info',});
      await subscribetoFluxGroup({
        variables: {
          id: parseInt(flux.id),frequency: 'all',});
    } else {
      addFav(flux);
      Notifier.showNotification({
        title: 'Vous êtes abonné à ce flux',});
      await subscribetoFlux({
        variables: {
          id: parseInt(flux.id),}
      });
    }
  };

  const handleRemoveFavorite = async (flux) => {
    if (flux.__typename === 'FluxGroup') {
      Notifier.showNotification({
        title: 'Vous êtes désabonné de ce groupe de flux',Component: CustomNotifierError,componentProps: {
          alertType: 'error',});
      await unsubscribeFromFluxGroup({
        variables: {
          id: parseInt(flux.id),update: (cache,{data}) => {
          const existingFavs = cache.readQuery({
            query: ALL_FAVORITES_QUERY,});
          //console.log('DATA UPDATE:',data);
          const newFavs = existingFavs.userFavorites.filter(
            (item) => item.id !== flux.id,);
          console.log('DATA UPDATE:',newFavs);
          cache.writeQuery({
            query: ALL_FAVORITES_QUERY,data: {userFavorites: [newFavs,...existingFavs.userFavorites]},});
        },});
    } else {
      Notifier.showNotification({
        title: 'Vous êtes désabonné de ce flux',});
      await unsubscribeFromFlux({
        variables: {
          id: parseInt(flux.id),});
    }
  };

  function sliceIntochunks(arr,chunkSize) {
    const res = [];
    for (let i = 0; i < arr.length; i += chunkSize) {
      const chunk = arr.slice(i,i + chunkSize);
      res.push(chunk);
    }
    return res;
  }

  useEffect(() => {
    if (error) {
      setIsLoading(true);
      setError(error.message);
      setIsLoading(false);
    }
  },[error]);

  const SeeMore = ({onPress}) => {
    return (
      <TouchableOpacity onPress={onPress}>
        <Text
          size={15}
          mr="sm"
          color={dark ? 'primary' : colors.text}
          style={styles.letSpacing}>
          Tout Voir
        </Text>
      </TouchableOpacity>
    );
  };

  const renderHeader = () => {
    if (slidesLoading) {
      return (
        <ScrollView
          horizontal
          showsHorizontalScrollIndicator={false}
          contentContainerStyle={{
            paddingHorizontal: theme.space.sm,paddingTop: theme.space.sm,height: HEIGHT / 4.8,justifyContent: 'center',alignItems: 'center',width: WIDTH,}}>
          <ActivityIndicator color={theme.color.primary} size={24} />
        </ScrollView>
      );
    }
    return (
      <>
        <ScrollView
          horizontal
          showsHorizontalScrollIndicator={false}
          contentContainerStyle={{
            paddingHorizontal: theme.space.sm,}}>
          {slidesData.explorer_slides.map((slide) => {
            const type = slide.item.__typename;
            return (
              <TouchableOpacity
                key={slide.id}
                onPress={() =>
                  navigation.navigate(
                    type === 'Flux'
                      ? 'SingleFlux'
                      : type === 'FluxGroup'
                      ? 'MultipleFlux'
                      : 'FluxCategory',{
                      headerTitle: slide.item.name,headerItem: slide.item,itemId: slide.item.id,headerText:
                        slide.item.__typename !== 'FluxCategory'
                          ? slide.item.description
                          : null,)
                }>
                <Box
                  mx="xs"
                  bg="primary"
                  w={WIDTH - 120}
                  h={150}
                  radius="sm"
                  align="center"
                  justify="center"
                  overflow="hidden">
                  <Image
                    source={{uri: slide.image.uri}}
                    style={styles.imgCat}
                    resizeMode="cover"
                  />
                </Box>
              </TouchableOpacity>
            );
          })}
        </ScrollView>
        <Box mt="md" h={1} w={WIDTH} bg={dark ? 'grey' : 'lightBorder'} />
      </>
    );
  };

  const renderItem = ({item,index}) => {
    return (
      <Box key={item - index} mb={8}>
        {item.map((section,index) => {
          const multiple = section.__typename === 'FluxGroup';
          const subscribed = section.subscribed;
          return (
            <TouchableOpacity
              key={section.id}
              onPress={() =>
                !multiple
                  ? navigation.navigate('SingleFlux',{
                      headerTitle: section.name,itemId: section.id,headerItem: section,subscribed: subscribed,itemExist: exists(section),})
                  : navigation.navigate('MultipleFlux',})
              }>
              <SubscribeItem
                flux={section}
                id={section.id}
                channel={section.name}
                title={
                  section.description
                    ? section.description
                    : `Toutes les actualités sur ${section.name}`
                }
                icon={section.image?.uri ? `${section.image?.uri}` : null}
                custom={section.customChannel}
                pushNumber={section.frequency_numbers_all}
                multiple={multiple}
                button={
                  <>
                    {/* <ToggleIcon
                    favorite={exists(section)}
                    onPress={() =>
                      exists(section)
                        ? handleRemoveFavorite(section)
                        : handleAddFavorite(section)
                    }
                  /> */}
                    <ToggleIcon
                      favorite={favExists(section)}
                      onPress={() =>
                        favExists(section)
                          ? handleRemoveFavorite(section)
                          : handleAddFavorite(section)
                      }
                    />
                  </>
                }
              />
            </TouchableOpacity>
          );
        })}
      </Box>
    );
  };

  const renderCategories = () => {
    if (!explorerData) {
      return (
        <Box py="sm">
          <Text mb="sm" center color="lightGrey">
            Catégories en chargement
          </Text>
          <Loading />
        </Box>
      );
    }
    if (explorerData) {
      return explorerData.explorer_categories.map((section) => {
        const sectionData = sliceIntochunks(section.related,3);
        return (
          <>
            <Box
              w={WIDTH}
              key={section.id}
              dir="row"
              justify="between"
              align="center">
              <Text
                size="xl"
                pt="sm"
                pb="2xs"
                ml="sm"
                color={dark ? 'white' : 'black'}
                style={styles.header}>
                {section.name}
              </Text>
              <SeeMore
                onPress={() =>
                  navigation.navigate('FluxCategory',{
                    headerTitle: section.name,headerText: null,})
                }
              />
            </Box>
            <Box>
              <FlatList
                horizontal
                pagingEnabled={true}
                showsHorizontalScrollIndicator={false}
                contentContainerStyle={styles.contentContainerStyle}
                data={section ? sectionData : []}
                renderItem={renderItem}
                exTradata={favoritesData}
                keyExtractor={(item,index) => item + index}
                onEndReachedThreshold={0}
              />
              <Box h={1} bg={dark ? 'grey' : 'lightBorder'} mb="sm" />
            </Box>
          </>
        );
      });
    }
  };

  if (error) {
    return (
      <Box f={1} justify="center" align="center">
        <Box mb="xs">
          <ErrorIcon
            name="cloud-offline-outline"
            color={dark ? theme.color.lightGrey : 'grey'}
            size={32}
          />
        </Box>
        <Text
          size="md"
          center
          color={dark ? 'lightGrey' : 'grey'}
          style={styles.letSpacing}>
          Une erreur s'est produite
        </Text>
        <Text
          size="sm"
          color={dark ? 'lightGrey' : 'grey'}
          style={styles.letSpacing}>
          Réessayez plus tard
        </Text>
        <TouchableOpacity onPress={() => refetch()}>
          <Box mt="sm">
            <RefreshIcon name="refresh" size={24} color={theme.color.primary} />
          </Box>
        </TouchableOpacity>
      </Box>
    );
  }

  if (isLoading) {
    return <Loading />;
  }

  return (
    <SafeAreaView
      style={[styles.container,{backgroundColor: colors.background}]}>
      <ScrollView showsverticalScrollIndicator={false}>
        <Box>{renderHeader()}</Box>
        {renderCategories()}
      </ScrollView>
    </SafeAreaView>
  );
};

const styles = StyleSheet.create({
  container: {
    width: WIDTH,flex: 1,searchBar: {
    width: WIDTH,backgroundColor: theme.color.secondary,borderBottomColor: theme.color.secondary,borderTopColor: theme.color.secondary,inputBar: {
    backgroundColor: theme.color.black,borderRadius: theme.space.md,header: {
    fontFamily: 'System',fontWeight: '700',letterSpacing: 0,icon: {
    width: 25,height: 25,borderRadius: 6,backgroundColor: theme.color.primary,overflow: 'hidden',iconNull: {
    width: 25,imgCat: {
    width: '100%',height: 150,letSpacing: {
    letterSpacing: 0,});
export default ExplorerScreen;

我错过了什么吗?还是我完全错了哈哈? 如果您需要有关我的代码的更多信息,请随时询问:)

解决方法

试试这个。删除项目或添加项目后,UI 将立即刷新:

    await unsubscribeFromFlux({
      variables: {
        id: parseInt(flux.id),},refetchQueries: GET_EXPLORER_SLIDES_QUERY
    });
,

事实证明,你不能持久化一个反应变量!所以我只是在突变后重新获取查询并更新我的缓存:) 现在一切都很好!谢谢!

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。