React Native:三种 onPress 操作后如何重新渲染FlatList?
解决FlatList根据预订已看/待看状态自动重新渲染的问题
问题背景
你已实现预订删除后FlatList自动刷新,但修改预订的watched状态(设为已看/待看)时,列表不会自动重新渲染。现有代码如下:
const WatchList = () => { const uid = auth.currentUser.uid; const docRef = doc(db, 'users', uid); const [user, setUser] = useState({}); const [watched, setWatched] = useState(true); const [text, setText] = useState('To watch'); const [filteredBookings, setFilteredBookings] = useState(bookings); const bookingsRef = collection(db, "booking"); const [bookings, setBookings] = useState({}); useEffect(() => { getUser(); getBookings(); },[]) const getUser = async () => { const snap = await getDoc(docRef) setUser({user, ...snap.data()}) } const getBookings = async () =>{ const q = query(bookingsRef, where("users","array-contains",auth.currentUser.uid)); const unsubscribe = onSnapshot(q, (querySnapshot) => { const a = []; querySnapshot.forEach((doc) => { a.push(doc.data()); }); setBookings(querySnapshot.docs); }); } const toggleSwitch = () =>{ if(!watched){ setFilteredBookings(bookings.filter(function(item){ return item.data().watched == true; })) setText('Watched'); } else { setText('To watch'); setFilteredBookings(bookings.filter(function(item){ return item.data().watched == false; })) } setWatched(previousState => !previousState); } const updateBooking = async(id) => { try { await setDoc(doc(db, 'booking', id), { watched: true }, {merge: true}) } catch(e) { console.log(e) } } const updateBooking1 = async(id) => { try { await setDoc(doc(db, 'booking', id), { watched: false }, {merge: true}) } catch(e) { console.log(e) } } const deleteBooking = async(id) => { try { await deleteDoc(doc(db, 'booking', id)); } catch(e) { console.log(e) } } useEffect(() => { const resultOfFiltering = bookings setFilteredBookings(resultOfFiltering) }, [bookings]) return ( <View> <View> <Text>{text}</Text> <Switch value = {watched} /> </View> <FlatList data = {filteredBookings} renderItem = {({item}) => ( <View> <View> <Text>{item.data().movie}</Text> <Text>{item.data().day} - {item.data().showtime}</Text> </View> <View> {item.data().watched == true ? ( <TouchableOpacity style = {styles.delete} onPress = {() => {setWatched(item.id); setFilteredBookings();}}> <Image source={require('../assets/watched.png')}/> </TouchableOpacity> ) : ( <TouchableOpacity onPress = {() => {setToWatch(item.id); setFilteredBookings();}}> <Image source={require('../assets/towatch.png')}/> </TouchableOpacity>)} <TouchableOpacity onPress = {() => {deleteBooking(item.id); setFilteredBookings();}}> <Image source={require('../assets/remove.png')}/> </TouchableOpacity> </View> </View> )} /> </View> ) } export default WatchList
解决方案
1. 重构过滤逻辑,绑定正确依赖
删除原toggleSwitch内的过滤代码和单独依赖bookings的useEffect,替换为同时监听bookings和watched状态的useEffect,自动计算过滤后的列表:
// 简化toggleSwitch逻辑,只负责切换状态和文本 const toggleSwitch = () => { setWatched(prev => !prev); setText(prev => prev === 'To watch' ? 'Watched' : 'To watch'); }; // 核心:根据实时数据和开关状态自动过滤列表 useEffect(() => { if (!bookings || bookings.length === 0) { setFilteredBookings([]); return; } const filtered = bookings.filter(item => item.data().watched === watched); setFilteredBookings(filtered); }, [bookings, watched]);
2. 修正按钮点击事件
将按钮中不存在的setWatched/setToWatch函数替换为你已定义的updateBooking/updateBooking1,并且移除手动调用setFilteredBookings——Firestore的onSnapshot会在数据更新时自动同步bookings状态,进而触发上面的useEffect重新过滤列表。
3. 修复初始状态与组件细节
- 初始
filteredBookings设为空数组:const [filteredBookings, setFilteredBookings] = useState([]); - 给
FlatList添加keyExtractor,避免渲染警告 - 给
Switch绑定onValueChange事件,确保开关能触发状态切换
修改后的完整代码
const WatchList = () => { const uid = auth.currentUser.uid; const docRef = doc(db, 'users', uid); const [user, setUser] = useState({}); const [watched, setWatched] = useState(true); const [text, setText] = useState('To watch'); const [filteredBookings, setFilteredBookings] = useState([]); const bookingsRef = collection(db, "booking"); const [bookings, setBookings] = useState([]); useEffect(() => { getUser(); const unsubscribe = getBookings(); // 组件卸载时清理Firestore监听 return () => unsubscribe(); }, []) const getUser = async () => { const snap = await getDoc(docRef) setUser(prev => ({...prev, ...snap.data()})) } const getBookings = async () =>{ const q = query(bookingsRef, where("users","array-contains",auth.currentUser.uid)); return onSnapshot(q, (querySnapshot) => { setBookings(querySnapshot.docs); }); } const toggleSwitch = () => { setWatched(prev => !prev); setText(prev => prev === 'To watch' ? 'Watched' : 'To watch'); }; // 统一状态更新函数,减少重复代码 const updateWatchedStatus = async (id, status) => { try { await setDoc(doc(db, 'booking', id), { watched: status }, { merge: true }) } catch(e) { console.error('更新状态失败:', e) } } const deleteBooking = async(id) => { try { await deleteDoc(doc(db, 'booking', id)); } catch(e) { console.error('删除预订失败:', e) } } useEffect(() => { if (!bookings || bookings.length === 0) { setFilteredBookings([]); return; } const filtered = bookings.filter(item => item.data().watched === watched); setFilteredBookings(filtered); }, [bookings, watched]) return ( <View> <View> <Text>{text}</Text> <Switch value={watched} onValueChange={toggleSwitch} /> </View> <FlatList data={filteredBookings} keyExtractor={(item) => item.id} renderItem={({item}) => ( <View> <View> <Text>{item.data().movie}</Text> <Text>{item.data().day} - {item.data().showtime}</Text> </View> <View> {item.data().watched ? ( <TouchableOpacity style={styles.delete} onPress={() => updateWatchedStatus(item.id, false)}> <Image source={require('../assets/watched.png')}/> </TouchableOpacity> ) : ( <TouchableOpacity onPress={() => updateWatchedStatus(item.id, true)}> <Image source={require('../assets/towatch.png')}/> </TouchableOpacity> )} <TouchableOpacity onPress={() => deleteBooking(item.id)}> <Image source={require('../assets/remove.png')}/> </TouchableOpacity> </View> </View> )} /> </View> ) } export default WatchList
原理说明
- Firestore的
onSnapshot会实时监听数据变化,当你调用updateWatchedStatus修改watched字段后,querySnapshot会触发更新,同步更新bookings状态。 - 依赖
bookings和watched的useEffect会在任意一个状态变化时重新执行,自动过滤出符合当前开关状态的预订列表,最终触发FlatList重新渲染。
内容的提问来源于stack exchange,提问作者Mihailo
相关产品推荐
相关产品推荐

