React Native实现Firebase列表筛选报Too many re-renders错误
问题根因
你碰到的Too many re-renders报错是多处写法错误共同触发的无限渲染循环:
onSnapshot数据监听器直接写在组件函数体中,没有用useEffect包裹:组件每次重渲染都会重新注册一次监听,监听回调里执行状态更新又会触发新一轮渲染,直接进入死循环。- 按钮
onPress绑定逻辑错误:你写的onPress={updateSearch(1)}会在组件渲染阶段直接执行updateSearch函数,而非等用户点击后触发,函数内调用setArr更新状态会再次触发渲染,渲染又执行updateSearch,持续触发循环。 - React的状态更新函数
useState返回的set方法不支持第二个参数传回调的写法,同时你直接修改state变量的属性(比如arr.data = xxx)属于可变更新,本身就违反React状态更新规则,会额外诱发渲染异常。 - 附带的冗余问题:从react导入了不存在的
setState方法,useNavigation声明后没有初始化调用,全量数据、临时筛选数组的状态逻辑完全混乱,没有实现你最初设计的「主数组存全量、临时数组做筛选」的逻辑。
修复方案
完全按照你最初的设计逻辑实现:Firebase拉取的全量数据存在主状态中,每次筛选从主数组拷贝数据做过滤,结果交给FlatList渲染,修正后的完整代码如下:
import React, { useState, useEffect } from 'react'; import { View, Text, StyleSheet, ImageBackground, FlatList, Button } from 'react-native' import Card from '../components/Card'; import colors from '../config/colors'; import {MaterialCommunityIcons} from '@expo/vector-icons' import { useNavigation } from '@react-navigation/core'; import { collection, query, where, onSnapshot } from 'firebase/firestore'; import {db} from '../../firebase'; // 活动类型映射:跑步1、游泳2、骑行3 function EventsPage(props) { const navigation = useNavigation(); // 主数组:存储Firebase拉取的全量活动数据 const [allEvents, setAllEvents] = useState([]); // 渲染数组:存储筛选后的展示结果 const [showEvents, setShowEvents] = useState([]); // Firebase监听放在useEffect中,仅组件挂载时注册1次,避免重复绑定 useEffect(() => { const colRef = collection(db, 'events'); const q = query(colRef, where('type', '>=', 0)); // 注册快照监听 const unSubscribe = onSnapshot(q, (snapshot) => { const eventList = []; snapshot.docs.forEach((doc) => { eventList.push({...doc.data(), id: doc.id}); }); setAllEvents(eventList); // 首次加载默认展示全部活动 setShowEvents(eventList); }); // 组件卸载时取消监听,避免内存泄漏 return () => unSubscribe(); }, []) // 筛选逻辑:仅用户点击按钮时执行 const handleFilter = (type) => { // 选全部时直接返回全量数据的拷贝 if (type === 0) { setShowEvents([...allEvents]); return; } // 从全量主数组拷贝数据做筛选,不修改原始源数据 const filteredResult = allEvents.filter(item => item.type === type); setShowEvents(filteredResult); } return ( <View style = {styles.container}> <ImageBackground source = {require('../assets/splash-page.jpg')} style = {{width: '100%', height: '100%'}} blurRadius={8} > <View style = {styles.backIcon}> <MaterialCommunityIcons name='arrow-left-bold' color="black" size={35} onPress={() => navigation.replace("Connect_me")} /> </View> <Text style = {styles.text}>EVENTS</Text> <View style = {styles.buttons}> {/* onPress用箭头函数包裹,避免渲染阶段直接执行回调 */} <Button title={'全部'} onPress={() => handleFilter(0)}/> <Button title={'跑步'} onPress={() => handleFilter(1)}/> <Button title={'游泳'} onPress={() => handleFilter(2)}/> <Button title={'骑行'} onPress={() => handleFilter(3)}/> </View> <FlatList data={showEvents} keyExtractor={item => item.id} renderItem={({ item }) => ( <View style = {styles.cardContainer}> <Card title={item.title} subTitle = {item.subTitle}/> </View> )} /> </ImageBackground> </View> ); } const styles = StyleSheet.create({ text: { fontSize: 80, textAlign:'center', paddingBottom: 20, color: colors.primary, fontWeight: 'bold', }, container: { backgroundColor: 'white', }, cardContainer: { width: '95%', alignSelf: 'center' }, backIcon: { paddingLeft: 10, paddingTop: 30, }, buttons: { flexDirection: 'row', padding: 20, justifyContent: 'space-around' } }) export default EventsPage;
关键修复点说明
- 所有数据拉取、事件监听类副作用统一放到
useEffect中,依赖数组传空值保证仅组件挂载时执行一次,从根源避免重复注册监听触发的渲染循环。 - 按钮点击事件统一用箭头函数包裹回调,只有用户实际点击时才会执行筛选逻辑,不会在渲染阶段误触发状态更新。
- 拆分状态逻辑:全量源数据和筛选后展示的数据分开存储,每次筛选都从全量数据做浅拷贝后过滤,不会直接修改原始状态,符合React单向数据流要求。
- 清理了无用的布尔标记、错误导入、空占位组件等冗余代码,给FlatList补充了
keyExtractor属性避免列表渲染警告。
内容的提问来源于stack exchange,提问作者suchafoool
相关产品推荐
相关产品推荐

