React Native中更新观影状态后,如何让FlatList正确重新渲染?
问题:切换观影列表状态时FlatList无法正确更新
我需要实现一个切换开关,在「To Watch」和「Watched」预约列表之间切换。更新「To Watch」列表中的电影后,希望FlatList重新渲染,该电影不再出现在此列表(已移至「Watched」列表);切换到「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"); // imamo ref ka bazi 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 = {() => {updateBookings(item.id); setFilteredBookings();}}> <Image source={require('../assets/watched.png')}/> </TouchableOpacity> ) : ( <TouchableOpacity onPress = {() => {updateBookings1(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. 核心错误点
- 函数名拼写错误:渲染代码中调用的
updateBookings/updateBookings1,实际定义的函数是updateBooking/updateBooking1,点击后无响应 - Switch未绑定切换逻辑:Switch组件缺少
onValueChange属性,点击开关不会触发状态切换 - 过滤逻辑冲突:监听
bookings变化的useEffect会直接覆盖过滤后的列表,导致切换开关后数据被重置 - 状态初始化错误:
filteredBookings初始值设为未定义的bookings,应该初始化为空数组[] - 无效状态更新:点击按钮时调用
setFilteredBookings()会把数据设为undefined,无需手动触发,Firestore的onSnapshot会自动更新数据
2. 修复后的完整代码
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({...user, ...snap.data()}) } const getBookings = () =>{ const q = query(bookingsRef, where("users","array-contains",auth.currentUser.uid)); return onSnapshot(q, (querySnapshot) => { setBookings(querySnapshot.docs); }); } const toggleSwitch = () =>{ const newWatchedState = !watched; setWatched(newWatchedState); setText(newWatchedState ? 'To watch' : 'Watched'); } 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(() => { if (!bookings.length) return; const filtered = bookings.filter(item => { return 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 === true ? ( <TouchableOpacity style={styles.delete} onPress={() => updateBooking(item.id)}> <Image source={require('../assets/watched.png')}/> </TouchableOpacity> ) : ( <TouchableOpacity onPress={() => updateBooking1(item.id)}> <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
3. 额外优化点
- 添加
keyExtractor给FlatList,避免React Native渲染警告 - 修正
getUser中的状态更新语法,确保正确合并用户数据 - 在
getBookings中返回订阅清理函数,防止组件卸载后内存泄漏
内容的提问来源于stack exchange,提问作者Mihailo
相关产品推荐
相关产品推荐

