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

保存页面时React Native FlatList重复渲染且报key重复错误如何解决?

问题根源

  1. 重复key警告:更新sessions状态时直接使用了当前闭包捕获的旧sessions值,多次触发数据加载或热更新重新触发初始化逻辑时,会将已存在的列表项重复拼接进数组,导致相同sessionID的条目重复出现,触发key冲突警告。
  2. 列表项重复渲染:RenderCard组件定义在HomeScreen内部,每次HomeScreen重渲染时都会生成新的RenderCard函数实例,FlatList会判定所有项的渲染组件发生变化,触发全量重渲染。

修复后代码

import React,{useState,useEffect, useCallback, memo} from 'react';
import { View, Text, StyleSheet, FlatList, ActivityIndicator, TouchableOpacity, Button } from 'react-native';
import firestore from '@react-native-firebase/firestore';
import { Icon } from "react-native-vector-icons/Ionicons";

// 将RenderCard移到组件外部,用memo包裹避免不必要重渲染
const RenderCard = memo(({item, navigation})=>{
  return(
    <TouchableOpacity onPress={()=>{navigation.navigate('HomeScreen2', {item})}}>
      <View style={{padding: 10}}>
        <Text>Title= {item.title}</Text>
        <Text>Description= {item.description}</Text>
      </View>
    </TouchableOpacity>
  )
})

const HomeScreen = ({navigation}) => {
  const [sessions,setSessions] = useState([]);
  const [sessionsPerLoad] = useState(12)
  const [startAfter,setStartAfter] = useState(null)
  const [lastPost,setLastPost] = useState(false)
  const [loading, setLoading] = useState(false) // 新增加载状态,避免并发请求

  const getSessions = async (count) => {
    const sessionArray = [];
    const querySnap = await firestore()
      .collection('sessions')
      .orderBy('createdAt', 'desc')
      .limit(count)
      .get()
    const lastVisible = querySnap.docs[querySnap.docs.length - 1]
    querySnap.forEach((doc)=> {
      let sessionData = doc.data()
      sessionData.sessionID = doc.id
      sessionArray.push(sessionData)
    })
    return {sessionArray, lastVisible}
  }

  const getMoreSessions = async (startDoc, count) => {
    const sessionArray = [];
    const querySnap = await firestore()
      .collection('sessions')
      .orderBy('createdAt', 'desc')
      .startAfter(startDoc)
      .limit(count)
      .get()
    const lastVisible = querySnap.docs[querySnap.docs.length - 1]
    querySnap.forEach((doc)=> {
      let sessionData = doc.data()
      sessionData.sessionID = doc.id
      sessionArray.push(sessionData)
    })
    return {sessionArray, lastVisible}
  }

  const getSession = useCallback(async () => {
    if(loading) return
    setLoading(true)
    const sessionsData = await getSessions(sessionsPerLoad)
    // 改用函数式更新,拿到最新的前序状态
    setSessions(prev => [...prev, ...sessionsData.sessionArray])
    setStartAfter(sessionsData.lastVisible)
    // 首次加载数据不足分页阈值,直接标记无更多数据
    if(sessionsData.sessionArray.length < sessionsPerLoad) {
      setLastPost(true)
    }
    setLoading(false)
  }, [sessionsPerLoad, loading])

  const getMoreSession = useCallback(async () => {
    if(lastPost || loading || !startAfter) return
    setLoading(true)
    const sessionsData = await getMoreSessions(startAfter, sessionsPerLoad)
    // 改用函数式更新,避免闭包旧值
    setSessions(prev => [...prev, ...sessionsData.sessionArray])
    setStartAfter(sessionsData.lastVisible)
    setLastPost(sessionsData.sessionArray.length === 0)
    setLoading(false)
  }, [startAfter, sessionsPerLoad, lastPost, loading])

  useEffect(()=>{
    getSession()
  },[getSession])

  return(
    <View>
      <FlatList
        data={sessions}
        renderItem={({item})=><RenderCard item={item} navigation={navigation} />}
        keyExtractor={(item)=>item.sessionID}
        onEndReached={getMoreSession}
        onEndReachedThreshold={0.01}
        scrollEventThrottle={150}
        ListFooterComponent={()=>
          !lastPost && loading && <ActivityIndicator />}
      />
    </View>
  );
};

const styles = StyleSheet.create({

});

export default HomeScreen

核心修改说明

  • 所有sessions状态更新改用函数式写法,彻底避免闭包捕获旧状态导致的数据重复拼接,解决key冲突警告
  • RenderCard移到组件外部并使用React.memo包裹,仅当item属性变化时才会重渲染对应列表项,大幅减少不必要的重渲染
  • 新增加载状态锁,避免并发触发分页请求导致数据重复
  • 修正startAfter初始值为null,避免首次查询异常
  • 首次加载时自动判断是否还有更多数据,减少无效请求

内容的提问来源于stack exchange,提问作者Nikhil Jain

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 17:57:01