You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 13:20:38