当前位置: 首页 > news >正文

为什么状态一集中,所有 RN 性能优化都会失效

@[toc]

为什么这是一类“怎么优化都没用”的问题

RN 列表性能问题里,有一类非常让人崩溃的场景:

  • 你已经:

    • 用了React.memo
    • 用了useCallback
    • 控制了keyExtractor
    • 甚至拆了子组件
  • 但:

    • 点一个按钮,列表还是会卡
    • 滑动时偶发掉帧
    • 性能分析一看,renderItem 还是在狂跑

很多人会下意识得出一个结论:

FlatList 不行
RN 性能差
JS 线程太慢

但如果你回头看这些项目,80% 都有一个共同点

状态被集中管理了。

而且集中得“非常合理”。

什么叫“状态一集中”

先说清楚概念。

典型的“状态集中”长这样

function ListPage() { const [listState, setListState] = useState({ likedMap: {}, selectedMap: {}, expandedMap: {}, }) return ( <FlatList data={data} renderItem={({ item }) => ( <Item item={item} liked={listState.likedMap[item.id]} selected={listState.selectedMap[item.id]} /> )} /> ) }

业务角度看,这段代码没有任何问题:

  • 状态统一
  • 数据集中
  • 管理方便

但从渲染模型看,它已经埋下了一颗性能炸弹。

集中的不是“数据”,而是“影响范围”

真正的问题不在于你“把状态放一起了”,而在于:

任何一个状态变更,影响的最小单位是整个 ListPage

也就是说:

任意一个 item 的交互 → ListPage rerender → FlatList rerender → 所有 renderItem 重新执行

这条链路一旦成立,后面所有优化都会变成装饰品

为什么 memo / useCallback 在这里几乎没用

这是很多人最困惑的地方。

React.memo 只能挡 props 不变的 rerender

const Item = React.memo(({ liked }) => { ... })

看起来好像能挡住重渲染,对吧?

但问题在于:

  • liked是从listState解构出来的
  • 每次setListStatelistState都是一个新对象
  • renderItem 每次都会重新执行

结果是:

memo 挡住了 Item 的 render 函数,但挡不住 renderItem 本身

而 renderItem 恰恰是 FlatList 最重的一层。

useCallback 只能解决“函数引用”,解决不了“依赖变化”

const onLike = useCallback(() => { ... }, [listState])

只要你依赖的是集中状态对象

  • callback 每次都会重新创建
  • 下游组件依然会 rerender

你会发现一个很讽刺的现象:

状态越集中,useCallback 的 dependency 越大,越没意义

一次 state 更新,是如何引发“渲染雪崩”的

这一节我们用一个非常具体的 Demo,把链路拆清楚。

Demo:一个点赞引发的全列表重算

function ListPage() { const [likedMap, setLikedMap] = useState<Record<string, boolean>>({}) const toggleLike = (id: string) => { setLikedMap(prev => ({ ...prev, [id]: !prev[id], })) } return ( <FlatList data={data} renderItem={({ item }) => { console.log('render item', item.id) return ( <Item item={item} liked={likedMap[item.id]} onLike={() => toggleLike(item.id)} /> ) }} /> ) }

点任意一个 item,你会在控制台看到:

render item 1 render item 2 render item 3 ... render item 100

哪怕你只点了第 57 个。

真正发生的事情(不是你以为的)

你以为发生的是:

第 57 个 item 状态更新 → 第 57 个 item rerender

但实际上发生的是:

setLikedMap → ListPage rerender → FlatList rerender → renderItem 全量执行 → JS 线程瞬间被占满

这就是渲染扩散

为什么 FlatList 的优化参数救不了你

很多人会继续尝试:

  • initialNumToRender
  • windowSize
  • removeClippedSubviews

但这些参数解决的是:

“一次渲染多少 item”

而不是:

“为什么会触发渲染”

当渲染触发点在 ListPage 时:

  • FlatList 已经没有选择权了
  • 它只能老老实实重新算一遍 renderItem

真正有效的分界线:状态的“最小归属单位”

所有性能优化是否有效,只取决于一件事:

状态变化,能不能被限制在最小使用单元内

不可优化的模型

状态 → ListPage

任何变化,影响整个列表。

可优化的模型

状态 → Item

变化只影响自己。

把状态“拆散”,为什么性能突然就好了

我们直接对比两种写法。

集中式(不可扩展)

<Item liked={likedMap[item.id]} />

分散式(可扩展)

const Item = React.memo(({ item }) => { const [liked, setLiked] = useState(false) })

此时渲染链路变成:

Item setState → 当前 Item rerender
  • ListPage 不动
  • FlatList 不动
  • 其他 item 完全无感

你会发现一个非常明显的变化:

甚至不需要 memo,性能就已经很好了

Redux / Context 为什么会“让一切优化失效”

Redux 的问题不是慢,而是“订阅面太大”

useSelector(state => state.list)

任何 list 内字段变化:

  • selector 返回新引用
  • 所有订阅组件 rerender

在列表场景下,这几乎等价于:

每次交互 = 全列表 rerender

Context 是最容易被低估的性能杀手

<ListContext.Provider value={listState}>

Context 的规则非常简单粗暴:

value 变了,所有 consumer 必须更新

在列表里,这意味着:

Context 更新 = renderItem 全跑

为什么“状态集中”在 Web 里没这么致命

这是一个很关键的对比点。

在 Web 里:

  • DOM diff 有天然的兜底
  • 浏览器渲染线程很强
  • 局部 repaint 成本低

但在 RN 里:

  • JS → Shadow Tree → Native
  • 每一次 rerender 都是跨线程协作
  • JS 线程一旦被拖慢,滑动立刻掉帧

所以:

RN 对“状态扩散”是零容忍的

总结

RN 里所有性能优化是否生效,只取决于一件事:
状态变化能不能被限制在足够小的范围内

一旦状态被集中:

  • memo 会失效
  • useCallback 会失效
  • FlatList 参数会失效
  • 你只剩下“体感卡顿”
http://www.jsqmd.com/news/186467/

相关文章:

  • AI开发工具----碾压 Bolt.new?Lovable:全栈开发的下一个“卷王”级神器深度评测
  • 揭秘Java应用性能拐点预测:如何用机器学习构建高精度运维模型
  • GitHub 热榜----86k+ Star!Awesome-LLM-Apps:可能是全网最好的 LLM 应用开发“实战圣经”
  • Elastic Stack梳理:深度解析Elasticsearch分布式查询机制与相关性算分优化实践 - 教程
  • 提升AI生成一致性:用lora-scripts定制固定输出格式的LLM模型
  • 假期网络钓鱼攻击中DocuSign伪装与虚假贷款诈骗的融合机制分析
  • JLink驱动固件升级步骤图解说明
  • 巴厘岛目的地婚礼推荐:唯一时光专做目的地婚礼的国内领先品牌深度解析——三亚目的地婚礼推荐/丽江目的地婚礼推荐/新疆目的地婚礼推荐/大理目的地婚礼推荐 - charlieruizvin
  • 网盘直链下载助手提速lora-scripts模型权重下载全过程
  • 计算机毕设java校园二手物品交易平台 基于Java的校园二手交易系统开发与实现 Java技术驱动的校园二手物品交易管理平台设计
  • 工业级打印系统中32位驱动主机的操作指南
  • android openCV 应用 (四)图像处理中的多尺度分析 - 指南
  • 计算机毕设java校园快递管理平台 基于Java的校园快递信息管理系统开发 Java技术驱动的校园快递管理平台设计与实现
  • 飞书多维表格管理lora-scripts项目进度与任务分配
  • 清华镜像源加速lora-scripts项目依赖库安装,告别下载超时
  • 【JavaDoc Markdown预览终极指南】:掌握高效文档编写的5大核心技巧
  • STM32CubeMX教程中的PWM生成配置通俗解释
  • STM32MP1双核配置实战案例:从零实现系统启动
  • 单片机驱动蜂鸣器:零基础实战操作指南
  • 1411.给 N x 3 网格图涂色的方案数
  • 自动标注+手动修正双模式:lora-scripts高效构建metadata.csv文件
  • 一点资讯个性化推送:根据用户画像发送lora-scripts资讯
  • 国产算力卡如寒武纪、昇腾能否运行lora-scripts?
  • 飞算JavaAI配置生成避坑指南,99%新手都会忽略的关键细节
  • 快速上手ARM Cortex-M ISR配置:新手入门步骤
  • 为什么你的 Spring Native 应用启动要3秒?这5个关键优化点你必须掌握
  • 国内用户福音:huggingface镜像网站助力lora-scripts模型拉取
  • STM32中CANFD和CAN的区别:一文说清通信机制差异
  • 2026年GEO服务商全景解析(2026年1月更新) - 品牌2025
  • Spring Native 为何无法超越传统JVM启动速度?深度剖析编译期优化盲区