记录一次react渲染优化

发布于:2025-08-13 ⋅ 阅读:(20) ⋅ 点赞:(0)

一、需要解决的问题

1.首次加载缓慢;
2.修改表单时,有时会修改失败。

二、问题分析

1.使用react-scan插件查看加载慢的问题,发现首次进入页面的时候会页面全屏重复加载多次的问题,每次修改表单内容都会触发整个应用的重新渲染。

2.重复的React.useEffect订阅,有多个useEffect同时订阅store的变化。

3.更新函数触发连锁反应,每当表单项发生变化时, `updateItem` 会更新areas,这触发了areas订阅,由于订阅的store是绑定的全局app.store所以会造成全局渲染。

三、问题解决

1.  优化订阅逻辑,减少不必要的更新触发
// 修改前
React.useEffect(
    () =>
      store.subscribe(
        (s) => s.areas,
        (areas) => {
          if (!task || step !== 1) return
          console.log('areas changed')
          updateTaskThrottledCallback({ ...task, areas })
        }
      ),
    [task, step]
  )

  React.useEffect(
    () =>
      store.subscribe(
        (s) => s.roiTransformer,
        (roiTransformer) => {
          if (!task || step !== 1) return
          console.log('roiTransformer changed')
          updateTaskThrottledCallback({ ...task, roiTransformer })
        }
      ),
    [task, step]
  )
  React.useEffect(
    () =>
      store.subscribe(
        (s) => s.imagePreprocessing,
        (imagePreprocessing) => {
          if (!task || step !== 1) return
          console.log('imagePreprocessing changed')
          updateTaskThrottledCallback({ ...task, imagePreprocessing })
        }
      ),
    [task, step]
  )

  React.useEffect(() => {
    if (!device) return
    setConfiguringStep(device.id, step)
    setTaskName(task?.name ?? '')
  }, [step])

// 修改后
  React.useEffect(
    () =>
      store.subscribe(
        (s) => ({ areas: s.areas, roiTransformer: s.roiTransformer, imagePreprocessing: s.imagePreprocessing }),
        ({ areas, roiTransformer, imagePreprocessing }) => {
          if (!task || step !== 1) return
          
          // 添加深度比较,避免因为对象引用变化而触发不必要的更新
          const hasRealChanges = 
            JSON.stringify(task.areas) !== JSON.stringify(areas) ||
            JSON.stringify(task.roiTransformer) !== JSON.stringify(roiTransformer) ||
            JSON.stringify(task.imagePreprocessing) !== JSON.stringify(imagePreprocessing)
          
          if (!hasRealChanges) return
          
          console.log('task data changed')
          updateTaskThrottledCallback({ ...task, areas, roiTransformer, imagePreprocessing })
        },
        {
          equalityFn: (a, b) =>
            a.areas === b.areas &&
            a.roiTransformer === b.roiTransformer &&
            a.imagePreprocessing === b.imagePreprocessing
        }
      ),
    [task, step]
  )
2. 拆解store

使用一个对应模块的store去拆解全局的app.store或者新建一个临时的store,在合适的时机(如点击完成按钮),去更新到全局的store.

3. 表单组件的优化

使用 useCallback 包裹了 handleItemChecked 、 handleItemChanged 和 handleFormatChange 等更新方法.

useCallback 是一个允许你在多次渲染中缓存函数的 React Hook。useCallback – React 中文文档

memo 允许你的组件在 props 没有改变的情况下跳过重新渲染。memo – React 中文文档

使用Memo包裹子组件

const Item = React.memo(({ item, itemIndex, handleItemChanged, handleFormatChange }) => {
  // 组件逻辑
})
4. 事件处理函数优化
// 优化前
const handleItemChecked = (itemIndex: number, checked: boolean) => {
  // 直接更新逻辑
}

// 优化后
const handleItemChecked = useCallback((itemIndex: number, checked: boolean) => {
  const item = tool.config.form.items[itemIndex]
  if (item.enabled === checked) return // 避免重复更新
  
  const updatedItem = { ...item, enabled: checked }
  tempFormStore.getState().addPendingUpdate(areaIndex, toolIndex, itemIndex, updatedItem)
}, [tool.config.form.items, areaIndex, toolIndex])
5. 订阅逻辑优化

缺乏深度比较,对象引用变化就会触发更新

没有检查数据是否真正发生变化

// 优化前
React.useEffect(
  () =>
    store.subscribe(
      (s) => ({ areas: s.areas, roiTransformer: s.roiTransformer, imagePreprocessing: s.imagePreprocessing }),
      ({ areas, roiTransformer, imagePreprocessing }) => {
        if (!task || step !== 1) return
        console.log('task data changed')
        updateTaskThrottledCallback({ ...task, areas, roiTransformer, imagePreprocessing })
      }
    ),
  [task, step]
)

// 优化后
React.useEffect(
  () =>
    store.subscribe(
      (s) => ({ areas: s.areas, roiTransformer: s.roiTransformer, imagePreprocessing: s.imagePreprocessing }),
      ({ areas, roiTransformer, imagePreprocessing }) => {
        if (!task || step !== 1) return
        
        // 添加深度比较,避免因为对象引用变化而触发不必要的更新
        const hasRealChanges = 
          JSON.stringify(task.areas) !== JSON.stringify(areas) ||
          JSON.stringify(task.roiTransformer) !== JSON.stringify(roiTransformer) ||
          JSON.stringify(task.imagePreprocessing) !== JSON.stringify(imagePreprocessing)
        
        if (!hasRealChanges) return
        
        console.log('task data changed')
        updateTaskThrottledCallback({ ...task, areas, roiTransformer, imagePreprocessing })
      }
    ),
  [task, step]
)
6. 区域组件渲染优化

乏精确的比较函数,自定义比较函数,只在真正需要更新时才重新渲染

const AreaItem = React.memo(({ areaIndex, area, onShowPanel }) => {
  // 组件逻辑
}, (prevProps, nextProps) => {
  // 自定义比较函数,只在真正需要更新时才重新渲染
  return (
    prevProps.areaIndex === nextProps.areaIndex &&
    prevProps.area.tools.length === nextProps.area.tools.length &&
    prevProps.area.points.length === nextProps.area.points.length &&
    JSON.stringify(prevProps.area.tools) === JSON.stringify(nextProps.area.tools) &&
    JSON.stringify(prevProps.area.points) === JSON.stringify(nextProps.area.points)
  )
})


网站公告

今日签到

点亮在社区的每一天
去签到