You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React中useMemo无法阻止组件重渲染的问题排查与解决

问题:React组件不必要重渲染的排查与解决

我在React应用中遇到了Video组件不必要重渲染的问题,相关代码如下:

Chat组件中渲染ChatMessage:

{Object.keys(storedUsers).length !== 0  && chatId && Object.keys(chatMessages).length !== 0  && Object.values(chatMessages).map((messageObj, index) => {
  return <ChatMessage key={index} messageObj={messageObj} />
})}

ChatMessage组件:

const ChatMessage = ({messageObj}) => { 
  // MEMOIZED VALUE
  const newMessageObj = useMemo(() => {return messageObj})

  if (messageObj.admin === true) return 

  const sentById = newMessageObj.sentBy
  let sentByUser
  let isOwn

  if (sentById === userData.userId) {
    sentByUser = userData
    isOwn = true
  }
  else {
    sentByUser = storedUsers &&  storedUsers[sentById] ||  newChatData  && newChatData.otherUsersData
  }

  return ( 
    <div className='d-flex flex-row' style={{marginTop: 50}}>
      <Video messageObject={newMessageObj} />
    </div>
  )
}

Video组件(已用memo包裹):

import React, { memo, useEffect } from 'react'

const Video = memo(({messageObject}) => {
  useEffect(() => {
    console.log('RE-RENDERED');
  }, [])
  return (
    <video>
      <source src={messageObject.url} />
    </video>
  )
})
export default Video

补充:获取chatMessages的Selector:

const chatMessages = useSelector((state) => state.messages.messagesData[chatId]) || {}

问题:当Chat组件中无关状态变更时,Video组件仍会重渲染。我已经尝试对传入Video的message对象做memo缓存,但无效,请问如何确保Video仅在挂载或messageObject.url变化时重渲染?是什么原因导致了不必要的重渲染?


原因分析
  • useMemo使用无效:你写的useMemo(() => messageObj)没有指定依赖项,且每次ChatMessage渲染时,messageObj是来自父组件的新引用,这个memo完全起不到缓存作用,newMessageObj和messageObj是同一个引用。
  • ChatMessage未被memo包裹:父组件Chat渲染时,所有ChatMessage都会被重新创建,哪怕它们的props没有变化,进而导致子组件Video跟着重渲染。
  • useSelector可能返回新对象:如果Redux状态更新时(哪怕是和当前chatId无关的消息变化),state.messages.messagesData[chatId]返回的是新的对象引用,chatMessages会触发更新,导致map重新生成ChatMessage组件。
  • Video的memo浅比较特性:memo默认是浅比较props,只要messageObject的引用变化,哪怕url没变,也会触发重渲染;另外你写的useEffect依赖是空数组,只会在组件挂载时执行,无法用来判断后续的重渲染。
  • 使用index作为key:用index作为组件key,当列表顺序变化或元素增删时,会导致组件错误地重渲染或挂载,也不利于性能优化。

解决方案
  1. 给ChatMessage组件添加memo包裹
    让ChatMessage只有在props变化时才重渲染:
const ChatMessage = memo(({messageObj}) => {
  // 原有代码
})

如果需要更精细的控制,可以自定义比较函数,比如只比较messageObj的关键属性:

const ChatMessage = memo(({messageObj}) => {
  // 原有代码
}, (prevProps, nextProps) => {
  const prevMsg = prevProps.messageObj;
  const nextMsg = nextProps.messageObj;
  return prevMsg.admin === nextMsg.admin &&
         prevMsg.sentBy === nextMsg.sentBy &&
         prevMsg.url === nextMsg.url;
})
  1. 优化useMemo的使用(或直接移除)
    既然已经给ChatMessage加了memo,没必要再在内部用useMemo缓存messageObj,直接使用props传入的messageObj即可;如果一定要用,需要指定正确的依赖项:
const newMessageObj = useMemo(() => messageObj, [messageObj.admin, messageObj.sentBy, messageObj.url])
  1. 优化useSelector的返回值稳定性
    使用Redux的shallowEqual来比较返回的对象,避免无关状态变更导致chatMessages引用变化:
import { useSelector, shallowEqual } from 'react-redux';

const chatMessages = useSelector((state) => state.messages.messagesData[chatId] || {}, shallowEqual)

或者用reselect创建一个记忆化的selector,确保只有当目标数据变化时才返回新引用:

import { createSelector } from '@reduxjs/toolkit';

const selectMessagesData = (state) => state.messages.messagesData;
const selectChatId = (state, chatId) => chatId;

const selectChatMessages = createSelector(
  [selectMessagesData, selectChatId],
  (messagesData, chatId) => messagesData[chatId] || {}
);

// 在组件中使用
const chatMessages = useSelector((state) => selectChatMessages(state, chatId))
  1. 给Video组件自定义memo比较逻辑
    让Video只在messageObject.url变化时重渲染:
const Video = memo(({messageObject}) => {
  useEffect(() => {
    console.log('RE-RENDERED (url changed or mounted)');
  }, [messageObject.url]) // 修改依赖,正确监听url变化
  return (
    <video>
      <source src={messageObject.url} />
    </video>
  )
}, (prevProps, nextProps) => {
  return prevProps.messageObject.url === nextProps.messageObject.url;
})
  1. 替换index为唯一key
    用messageObj的唯一标识(比如id)作为key,避免不必要的组件重构:
Object.values(chatMessages).map((messageObj) => {
  return <ChatMessage key={messageObj.id} messageObj={messageObj} />
})

内容的提问来源于stack exchange,提问作者Coding Ninja123211

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 09:44:51