为什么用useCallback包裹的回调函数传给子组件后还是触发重渲染?
我在React组件里用useCallback包裹了一个点击处理函数,然后传给子组件。但每次父组件更新时,子组件还是会重新渲染,明明依赖数组里啥都没放啊。之前在Vue里直接用methods传函数就不会这样,这是React的useCallback哪里用错了?
比如我这样写的Vue代码就没问题:
export default {
methods: {
handleClick() {
console.log('Vue里子组件不重渲染');
}
}
}
但React代码里用了useCallback还是不行:
父组件:
子组件的props接收onClick后,只要父组件有其他状态更新(比如计数器+1),子组件就会重新渲染,控制台也跟着打印了。难道不是应该只在依赖变化时才更新吗?
在你的代码里,虽然用useCallback包裹了函数,确保了函数引用不变,但如果你的子组件是用function直接定义的(比如const Child = () => {}),每次父组件渲染时,这个子组件的定义会被重新创建,导致它内部的状态和逻辑也会重新执行。
解决方法有两种,你可以根据实际情况选一个:
第一种是用React.memo包裹子组件。这个方法会浅比较props,如果props没变,子组件就不会重新渲染。代码可以这么改:
第二种是把子组件单独抽成一个文件导出,这样它的定义不会受父组件渲染的影响。比如新建一个Child.js:
这两种方法都能解决问题,不过要注意,React.memo只能浅比较props,如果你的props是复杂对象或者函数,可能还需要额外处理。
最后提醒一下,Vue的响应式机制和React不一样,Vue的methods默认就是稳定的,而React需要我们手动优化,这也是为啥你在Vue里没遇到这个问题的原因。
useCallback确实能保证函数引用不变,但子组件重渲染的原因可能不在这里。React的子组件默认会跟着父组件更新而重新渲染,即使传进去的props没变。你可以试试这样:给子组件加上
React.memo包裹。这样它就会做一层浅比较,如果传入的props没变化,就不会重新渲染了。比如这样写:
另外说一句,Vue里的确是直接用methods传函数不会导致子组件重渲染,因为Vue的渲染机制和React不一样。React这边就得手动优化一下啦,虽然稍微麻烦点,但也是为了更精确地控制性能嘛~