Reactfire未显示Firestore中的实时更新



我正在努力了解Firestore/Rectfire,所以我正在开发一个基本的todo应用程序来帮助我掌握这些概念。

我有一个screen/Screens/Mainscreen.js,它加载用户的所有待办事项列表。顶部有一个输入和一个添加新列表的按钮。这一切都有效,除了新列表没有加载到屏幕上吗?如果我刷新屏幕,则会加载新列表。我似乎错过了Reactfire的一些工作原理,但我的代码似乎遵循了所有的演示文档,所以我不确定我做错了什么。

此处为完整WIP项目:https://github.com/warm--tape/todo/(如果需要,在中添加带有Firebase凭据的.env(

这是我的MainScreen.js:

function MainScreen({ navigation }) {
// Logout action. Probably move.
const auth = useAuth();
function handleLogOut() {
auth.signOut().then(() => navigation.replace('AuthScreen'));
}
// Load User
const { status: userStatus, data: user } = useUser();
const { uid } = user;
// Load User Lists from Firestore
const firestore = useFirestore();
const listCollection = collection(firestore, 'lists');
const userListQuery = query(listCollection, where('access', 'array-contains', uid || 0));
const { status: listStatus, data: rawListData } = useFirestoreCollectionData(userListQuery, { idField: 'id' });
// Load toast
const toast = useToast();
// Set up state  
const [isLoading, setIsLoading] = useState(true);
const [listData, setListData] = useState([]);
const [listToAdd, setListToAdd] = useState({ listName: '' });
const [itemToAdd, setItemToAdd] = useState({ itemName: '' });
const [errors, setErrors] = useState({});
// Hide screen until loaded
if (isLoading && userStatus === 'success' && listStatus === 'success') {
setListData(rawListData);
setIsLoading(false);
}
// Form Validator
const validate = () => {
// Currently no validation
return true;
};
// Handle add list
async function onAddList() {
if (validate()) {
delete listToAdd.NO_ID_FIELD;
const listToAddData = { ...listToAdd, ...{ owner: uid, access: [uid] } };
await addDoc(collection(firestore, "lists"), listToAddData).then(() => {
toast.show({
title: "List Added",
placement: "bottom"
});
setListToAdd({ listName: '' });
});
} else {
alert('Validation Failed')
}
};
// Handle press on list
function onPressListHandler(id) {
navigation.navigate('ListDetailScreen', {listId: id})
}
// =========================================================================
// Render loading spinner
if (isLoading) {
return (
<LoadingSpinner />
);
}
// =========================================================================
// Render
const listRenderItem = ({ item }) => (
<Pressable
onPress={()=>{onPressListHandler(item.id)}}
borderBottomWidth="1" _dark={{borderColor: "gray.600"}} borderColor="coolGray.200" pl="4" pr="5" py="2"
>
<HStack space={3} justifyContent="start" alignItems="center">
<IconButton variant="unstyled" icon={<Icon as={Ionicons} name="list-outline" size="sm" />} onPress={() => {}} />
<Text _dark={{color: "warmGray.50"}} color="coolGray.800">
{item.listName}
</Text>
</HStack>
</Pressable>
);

// =========================================================================
// Render
return (
<ScreenWrapper>
<HStack>
<FormControl isRequired isInvalid={'listName' in errors}>
<Input placeholder="Add List..." value={listToAdd.listName} onChangeText={value => setListToAdd({ ...listToAdd, ...{ listName: value } })} />
{'listName' in errors ? <FormControl.ErrorMessage>{errors.listName}</FormControl.ErrorMessage> : null}
</FormControl>
<Button onPress={() => { onAddList() }} >
<Icon color="white" as={Ionicons} name="add" size="sm" />
</Button>
</HStack>
<Divider my="3" />
<Box>
<FlatList
data={listData}
renderItem={listRenderItem}
keyExtractor={item => item.id} />
</Box>
<Divider my="3" />
<Button onPress={() => handleLogOut()}>Logout</Button>
</ScreenWrapper>
);
}
export default MainScreen;

事实证明我的代码结构很糟糕,所以在深入查看了一些演示和文档后,以下重构工作正常:

import React, { useState } from 'react';
import { useFirestore, useUser, useFirestoreCollectionData } from 'reactfire';
import { addDoc, collection, query, where } from 'firebase/firestore';
import {
Text,
Button,
FlatList,
HStack,
Pressable,
Icon,
FormControl,
Input,
IconButton,
useToast,
Divider
} from 'native-base';
import { Ionicons } from '@expo/vector-icons';
import ScreenWrapper from '../components/ScreenWrapper';
import LoadingSpinner from '../components/LoadingSpinner';
function onPressListHandler(id) {
navigation.navigate('ListDetailScreen', { listId: id })
}
const listRenderItem = ({ item }) => (
<Pressable
onPress={() => { onPressListHandler(item.id) }}
borderBottomWidth="1" _dark={{ borderColor: "gray.600" }} borderColor="coolGray.200" pl="4" pr="5" py="2"
>
<HStack space={3} justifyContent="start" alignItems="center">
<IconButton variant="unstyled" icon={<Icon as={Ionicons} name="list-outline" size="sm" />} onPress={() => { }} />
<Text _dark={{ color: "warmGray.50" }} color="coolGray.800">
{item.listName}
</Text>
</HStack>
</Pressable>
);
const ListsList = () => {
// Load User
const { status: userStatus, data: user } = useUser();
const { uid } = user;
// Load User Lists from Firestore
const firestore = useFirestore();
const listCollection = collection(firestore, 'lists');
const userListQuery = query(listCollection, where('access', 'array-contains', uid || 0));
const { status: listStatus, data: lists } = useFirestoreCollectionData(userListQuery, { idField: 'id' });
// Loading lists
if (userStatus === 'loading' || listStatus === 'loading') {
return <LoadingSpinner />;
}
// Render
return (
<>
<FlatList
data={lists}
renderItem={listRenderItem}
keyExtractor={item => item.id}
/>
</>
);
};
function MainScreen({ navigation }) {
// Load toast
const toast = useToast();
const [listToAdd, setListToAdd] = useState({ listName: '' });
const [errors, setErrors] = useState({});
// Form Validator
const validate = () => {
// None needed here.
return true;
};
// Handle add list
async function onAddList() {
if (validate()) {
delete listToAdd.NO_ID_FIELD;
const listToAddData = { ...listToAdd, ...{ owner: uid, access: [uid] } };
await addDoc(collection(firestore, "lists"), listToAddData).then(() => {
toast.show({
title: "List Added",
placement: "bottom"
});
setListToAdd({ listName: '' });
});
} else {
alert('Validation Failed')
}
};

// =========================================================================
// Render
return (
<ScreenWrapper>
<HStack>
<FormControl isRequired isInvalid={'listName' in errors}>
<Input placeholder="Add List..." value={listToAdd.listName} onChangeText={value => setListToAdd({ ...listToAdd, ...{ listName: value } })} />
{'listName' in errors ? <FormControl.ErrorMessage>{errors.listName}</FormControl.ErrorMessage> : null}
</FormControl>
<Button onPress={() => { onAddList() }} >
<Icon color="white" as={Ionicons} name="add" size="sm" />
</Button>
</HStack>
<Divider my="3" />
<ListsList />
</ScreenWrapper>
);
}
export default MainScreen;

最新更新