React Native应用添加useEffects后卡顿无响应,如何解决?
解决React Native应用卡顿与无限重渲染问题
1. 修复数据库实例重复创建导致的无限查询
问题:const db = SQLite.openDatabase('db1.db')写在组件内部,每次组件渲染都会生成新的数据库实例,导致第一个useEffect的依赖[db]每次变化,触发无限次数据库查询,直接拖慢应用响应速度。
解决:将数据库实例移到组件外部,确保全局只创建一次;或者用useMemo缓存实例,避免重复生成。
修改后代码:
// 移到组件外部,全局仅初始化一次 const db = SQLite.openDatabase('db1.db'); const App = () => { // ... 其他代码 }
同时调整第一个useEffect的依赖,仅在组件挂载或日期变化时查询数据:
useEffect(() => { db.transaction(tx => { tx.executeSql(`SELECT * FROM Meals${date}`, null, (txObj, { rows: { _array } }) => { setSqlData(_array); }) }) }, [date]); // 仅依赖日期,日期变化时重新查询
2. 用SQL聚合函数直接计算Macros,减少客户端数据处理
问题:先拉取全量数据再通过forEach计算总和,当数据量大时会占用大量CPU和内存,同时每次sqlData更新都会触发重计算,增加渲染压力。
解决:直接在SQL查询中用SUM聚合函数计算宏量总和,一次查询得到结果,无需客户端处理全量数据。
修改查询逻辑(如果同时需要原始数据和总和,可以拆分为两次查询):
useEffect(() => { db.transaction(tx => { // 直接查询宏量总和 tx.executeSql( `SELECT SUM(protein) as protein, SUM(calories) as calories, SUM(fats) as fats, SUM(sugar) as sugar FROM Meals${date}`, null, (txObj, { rows: { _array } }) => { // 处理空表情况,避免null值 const result = _array[0] || { protein:0, calories:0, fats:0, sugar:0 }; setMacros({ protein: result.protein || 0, calories: result.calories || 0, fats: result.fats || 0, sugar: result.sugar || 0, }); }) }) }, [date]);
此修改可直接移除第二个计算macros的useEffect,减少一次状态更新和重渲染触发。
3. 优化组件渲染,避免不必要的重渲染
问题:
getWidget函数每次渲染都会重新定义,导致renderItem内的组件频繁重渲染;macros是对象类型,每次更新都会生成新引用,触发所有Rings4组件重渲染;renderItem函数未缓存,拖拽或数据变化时会大量重建组件。
解决:
- 用
useCallback缓存getWidget和renderItem函数; - 用
React.memo包裹Rings4、RecentMeals组件,避免props未变化时的重渲染。
修改代码:
import { useEffect, useState, useCallback, useMemo } from "react"; // 缓存getWidget函数 const getWidget = useCallback((type) => { switch(type){ case 0: return <Rings4 macros={macros} />; case 1: return <RecentMeals />; default: return <></>; } }, [macros]); // 缓存renderItem函数 const renderItem = useCallback(({item, drag, isActive}) => { return ( <ScaleDecorator activeScale={1.05}> <Pressable onLongPress={drag} disabled={isActive}> {getWidget(item.type)} </Pressable> </ScaleDecorator> ) }, [getWidget]);
在组件文件中添加React.memo:
// Rings4组件 import React from 'react'; const Rings4 = React.memo(({ macros }) => { // 原有组件逻辑 }); export default Rings4; // RecentMeals组件同理 const RecentMeals = React.memo(() => { // 原有组件逻辑 }); export default RecentMeals;
4. 缓存日期字符串,避免重复计算
问题:日期字符串每次渲染都会重新生成,虽影响不大,但可通过useMemo缓存减少不必要的计算。
解决:
const date = useMemo(() => { const today = new Date(); return `${today.getDate()}_${today.getMonth() + 1}_${today.getFullYear()}`; }, []);
5. 额外优化建议
- 如果
RecentMeals需要展示大量数据,改用虚拟列表(如react-native-virtualized-list),减少同时渲染的组件数量; - 若数据库数据量极大,考虑分批查询,避免一次性加载过多数据;
- 检查
DraggableFlatList的动画配置,关闭不必要的动画效果以降低性能开销。
内容的提问来源于stack exchange,提问作者Thomas Rooskens
相关产品推荐
相关产品推荐

