在使用 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',
Component: CustomNotifier,
componentProps: {
alertType: 'info',
},
});
await subscribeToFlux({
variables: {
id: parseInt(flux.id),
frequency: 'all',
}
});
}
};
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',
Component: CustomNotifierError,
componentProps: {
alertType: 'error',
},
});
await unsubscribeFromFlux({
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]},
});
},
});
}
};
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,
paddingTop: theme.space.sm,
height: HEIGHT / 4.8,
}}>
{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', {
headerTitle: section.name,
itemId: section.id,
headerItem: section,
subscribed: subscribed,
itemExist: exists(section),
})
}>
<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,
headerItem: section,
itemId: section.id,
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,
justifyContent: 'center',
alignItems: 'center',
},
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,
height: 25,
borderRadius: 6,
backgroundColor: theme.color.primary,
overflow: 'hidden',
},
imgCat: {
width: '100%',
height: 150,
},
letSpacing: {
letterSpacing: 0,
},
});
export default ExplorerScreen;
我错过了什么吗?还是我完全错了哈哈? 如果您需要有关我的代码的更多信息,请随时询问:)
答案 0 :(得分:0)
试试这个。删除项目或添加项目后,UI 将立即刷新:
await unsubscribeFromFlux({
variables: {
id: parseInt(flux.id),
},
refetchQueries: GET_EXPLORER_SLIDES_QUERY
});
答案 1 :(得分:0)
事实证明,你不能持久化一个反应变量!所以我只是在突变后重新获取查询并更新我的缓存:) 现在一切都很好!谢谢!