为什么React中推荐传递命名函数引用而非内联匿名函数作为事件监听props?
关于React重渲染与props的两个疑问解答
先直接给你明确答案,再展开细节:
1. 父组件重渲染时子组件本就会重渲染吗?
是的,默认情况下父组件重渲染,它的所有子组件(包括嵌套子组件)都会跟着重渲染,不管子组件的props有没有变化。这是React的默认渲染逻辑——React会假设父组件状态变化可能影响所有子组件,因此会递归触发子组件重渲染,以此保证UI和数据的一致性。
但这种默认行为可以优化,比如用React.memo包裹函数式子组件:如果子组件的props没有变化,就跳过重渲染。这也是为什么不推荐传递内联函数作为props的核心原因——内联函数每次都是新的引用,会让React.memo误以为props发生了变化,从而触发不必要的重渲染。
2. 函数式组件中,props更新是如何引发子组件重渲染的?
React内部会对组件的props做浅比较:
- 父组件重渲染时,会给子组件传递新的props值
- React会对比新props和子组件上一次接收的props:基本类型(字符串、数字、布尔值)比较值是否相等;引用类型(对象、数组、函数)比较引用地址是否相同
- 一旦浅比较发现props有变化,就会触发子组件重渲染——重新执行函数式组件的函数体,生成新的虚拟DOM,再和旧虚拟DOM做Diff对比,最后只把变化的部分更新到真实DOM上
关于你给出的两种函数传递方式的补充
你写的两种方式,在避免子组件重渲染上效果其实是一样的:
- 内联箭头函数:每次父组件重渲染都会创建新的箭头函数实例
- 组件内部定义的命名函数
changeTitle:每次父组件重渲染时,这个函数也会被重新创建(因为函数式组件每次重渲染都会重新执行内部所有代码)
如果想要真正避免因函数引用变化导致的子组件重渲染,需要用useCallback钩子缓存函数引用:
import React, { useCallback } from 'react'; import ExpenseDate from './ExpenseDate'; import Card from '../UI/Card'; import './ExpenseItem.css'; const ExpenseItem = (props) => { // 用useCallback缓存函数,只有依赖项变化时才会重新创建 const changeTitle = useCallback(() => { console.log("Hello"); }, []); // 空数组表示永远不重新创建函数引用 return ( <Card className='expense-item'> <ExpenseDate date={props.date} /> <div className='expense-item__description'> <h2>{props.title}</h2> <div className='expense-item__price'>${props.amount}</div> </div> <button onClick={changeTitle}>Change title</button> </Card> ); } export default ExpenseItem;
这样,除非useCallback的依赖数组发生变化,否则changeTitle的引用会一直保持不变,配合React.memo包裹子组件,就能有效避免不必要的重渲染。
内容的提问来源于stack exchange,提问作者user22473407
相关产品推荐
相关产品推荐

