为何React memo自定义相等函数需使用JSON.stringify?
问题
我有一个如下的ChatMessage组件:
const ChatMessage = memo(({messageObj, replyTo, newChatData, storedUsers, handleDeleteMessage, chatId}, arePropsEqual)
其中messageObj是类似{id: 3, text: "test"}的普通JavaScript对象。
自定义相等函数arePropsEqual在添加JSON.stringify后能正常工作,仅当messageObj的实际值变化时才重新渲染ChatMessage:
function arePropsEqual(oldProps, newProps) { // 如预期输出true console.log(JSON.stringify(oldProps.messageObj) === JSON.stringify(newProps.messageObj)); return JSON.stringify(oldProps.messageObj) === JSON.stringify(newProps.messageObj) }
而此前直接使用oldProps.messageObj === newProps.messageObj的相等函数无法按预期阻止重渲染。请问为何需要JSON.stringify?它为何能解决普通JavaScript对象的比较问题?
原因解析
1. 直接用===无效的核心原因
JavaScript里,===对对象这类引用类型的比较逻辑是看内存地址,不是看内容:
- 哪怕两个对象的键值完全一样,只要它们是内存里的不同实例,
===就会返回false。 - 比如父组件每次渲染时,如果
messageObj是新生成的对象(哪怕内容和之前没差),它的引用地址就变了,这时候oldProps.messageObj === newProps.messageObj会返回false,导致memo误以为属性变了,触发不必要的重渲染。
2. JSON.stringify的作用原理
JSON.stringify会把对象转换成对应的JSON字符串,这个字符串只和对象的实际键值内容绑定:
- 只要两个对象的键值对完全一致(键的顺序也一致时),它们的JSON字符串就完全相同,用
===比较字符串就能得到true。 - 这样就绕过了引用地址的对比,直接校验对象的实际内容,实现了只有当
messageObj内容真的变化时,才让memo允许组件重渲染的效果。
注意事项:JSON.stringify的局限
这个方法能解决当前问题,但存在一些潜在问题:
- 如果对象的键顺序不同(比如
{a:1, b:2}和{b:2, a:1}),JSON.stringify的结果会不一样,导致误判。 - 对象里如果包含
undefined、函数、Symbol类型的值,JSON.stringify会直接忽略这些属性,可能导致比较结果不准确。 - 性能开销:如果对象体积大,频繁调用
JSON.stringify会增加性能消耗,不如用专门的深比较工具(比如lodash的isEqual)高效。
内容的提问来源于stack exchange,提问作者Coding Ninja123211
相关产品推荐
相关产品推荐

