type
status
date
slug
summary
tags
category
icon
password

Rendered more hooks than during the previous render

出现在需要兼容 Android 和 IOS 的时候,有一个组件的某个属性只希望在安卓上面有,而不希望在 IOS 上面有,就写成了这样:
问题:这里使用了条件性的 props 展开,在安卓和 iOS 平台上 RecyclerView 接收到的 props 不同,这可能导致:
  • 组件内部状态不一致:RecyclerView 在安卓端有额外的 onRefresh 和 refreshing props
  • 内部 hooks 调用差异:不同的 props 可能触发组件内部不同的 hooks 逻辑
仅在安卓上传递了 onRefresh 和 refreshing 这些 props,可能触发了内部的 useEffect、useState 等 hooks
修改:统一双端 props:
使用 useMemo 的时候,有一个变量在外部被计算但被用作依赖:
问题:
  • useMemo 的依赖数组 [bottom] 引用了外部变量
  • React 无法正确追踪这个依赖的变化
  • 可能导致 useMemo 在某些渲染中被跳过或额外调用
修改:将计算逻辑移动到 useMemo 内部:

useCallback 和 useMemo 和 memo 什么时候用

useMemo

一般使用场景:
  • 计算量比较大
    • 计算量可能不大,但是要分成很多步才可以得到一个结果

      useCallback

      一般使用场景:
      • 遇到性能问题,有函数需要传给子组件、并且子组件有用 React.memo 包裹的时候才有效果
      • 该函数是某些 hook 的依赖项
      一般原理:
      • useCallback 会缓存函数本身
      • useCallback 并不会阻止函数的创建,render时总会创建一个函数,但 React 会忽略它,并在没有任何依赖项改变的情况下返回一个缓存的函数
      • 如果一个函数作为 prop 传递给子组件,每次父组件重新渲染时,子组件会认为这个函数发生了变化,从而触发子组件的重新渲染。useCallback 可以确保传递给子组件的函数实例在依赖项不变的情况下不会改变,从而避免不必要的渲染。

      memo

      是 react 的高阶组件,高阶组件就是参数为组件,返回值为新组件的组件。
      这个例子中,只有当 data 发生改变时,Child 组件才会重新被渲染。

      小结论

      • 计算量特别大的时候,像文档所说,创建或遍历数千个对象,要使用 useMemo。其它情况用不用看个人习惯。
      • 子组件重新渲染造成的影响很大的时候,子组件包裹 memo,并且传递给子组件的函数用 useCallback 包裹。否则使用 useCallback 几乎没有作用。
      • 开发了一个类似 react-useahooks 的 npm 包,返回的函数方法要包裹 useCallback,使用的人可能需要做优化。

      FlatList / ScrollView

      ScrollView:
      • 一次性渲染全部子元素的滚动容器
      • 元素过多时会卡顿
      • 官方文档建议:长列表不要用 ScrollView,要用 FlatList 或 SectionList
      FlatList:
      • 基于 ScrollView 封装的虚拟化长列表组件
        • 只渲染屏幕区域 + 前后一定缓冲区的内容
        • 滚动时卸载不可见元素并复用它们的组件实例
      • 适合长列表、大数据量的场景,比如聊天、商品列表、Feed流
      • 默认会控制渲染批次、窗口大小等(initialNumToRender, maxToRenderPerBatch, windowSize 等属性)
      ScrollView 使用场景:
      • 列表数量少且固定
        • 表单页面、图文详情页、设置页面
        • 实现简单、可控性高,不需要配置虚拟化参数
      • 列表和非列表内容混排
        • 如果用 FlatList,需要用 ListHeaderComponentListFooterComponent 来拼凑,反而结构更复杂
      • 需要一次性获取列表全部高度
        • 有些动画、滚动联动效果(比如滚动到一定位置吸顶)需要立即知道全部高度
      • 嵌套滚动 / 横向滚动
        • 例如外层是纵向 ScrollView,内部是横向的图片 ScrollView
        • 多个虚拟化列表同时存在时,React Native 需要同时维护多个渲染窗口、多个回收机制
        • 滚动事件多层传递会增加 JS 线程和原生 UI 线程的通信开销,可能导致掉帧
       
       
       
       
       
       
       
       
       
       
      实习项目梳理并发/并行/进程/线程
      • Giscus