我们为什么说组件通信是 React 的核心
- React 这门框架的核心是组件化,任何一个完整的 React 应用,都是由多个独立组件协同工作构成的,组件之间的协同,其本质就是数据传递,共享与同步 ,这也就是我们常说的 组件通信
- React严格遵循单向数据流原则:数据只能自上而下从父组件传递到子组件,状态的修改只能由持有该状态的组件完成。
在本文中,我将从基础到高阶再讲到一些特殊场景,来聊一聊 React 组件间通信的所有方案,可供新手入门及老手查缺补漏 (本文我所使用的技术栈为 React19 + 函数组件 + Hooks)
一、基础入门篇 父子组件间的通信 (为 React 中最常用的一种方式)
父子组件是 React 中最常见的组件关系,所有通信方式的底层逻辑,都基于父子通信的规则延伸而来
1.1 父向子传值
实现原理: 父组件直接在子组件的标签上绑定属性值,子组件通过参数props接受 ,此时该数据在子组件中仅为可读状态,不可修改,但该数据状态对象的属性值可以进行修改
代码实现:



若子组件直接修改父组件传递的状态对象,会报错


父向子传值的使用场景:
- 适用于所有父子组件之间的数据传递,是 React 组件通信的第一选择
- 当子组件的渲染内容完全由父组件决定时
- 当需要父组件向子组件传递事件处理函数
父子组件传值注意点:
- 禁止直接修改props,props是只读的,直接修改对象/数组类型的props,会破坏单向数据流,且不会触发组件重新渲染
- 需要避免透传地狱:不要为了给孙组件传值,让中间所有层级的组件都接力传递props,后文我会讲解解决方案
- 引用类型 props 注意重新渲染: 如果 props 传递的是对象/数组/函数,父组件每次重新渲染都会生成新的引用,导致子组件不必要的重新渲染,可以通过使用
useMemo/useCallback优化
1.2 子向父传值 (通过回调函数使子组件与父组件同步)
实现原理:利用 props 可以传递函数的特点,父组件可以通过将状态修改函数把 props 传递给子组件,而子组件触发该函数并传入参数,可实现子组件向父组件同步数据,但其本质仍为单向数据流的延伸
代码实现:
----------------通过简单的子向父传值实现通过改变子组件来改变父组件的内容--------------- 

子向父传值的使用场景:
- 当子组件需要向父组件传递事件触发信息时
- 当子组件需要修改父组件的状态时
- 当需要实现表单类子组件的输入值同步时
父子组件传值需要注意的点
- 闭包陷阱:回调函数中如果依赖父组件的状态,则需要注意闭包问题,可通过
useCallback+ 正确的依赖数组来解决 (具体解决方法将在后续更新文章中提到)
2.避免频繁触发回调:例如当使用onChange实时触发回调时,可能会导致父组件频繁重渲染,可以通过添加防抖来优化性能 3. 不要在子组件内修改回调函数的返回值: 为了保持单向数据流,请让子组件只负责触发回调,而不处理状态的修改逻辑,以便增加代码的可读性
1.3 父子直接通信:Ref 转发
实现原理:
-
让子组件"打开后门" :使用
forwardRef把父组件的ref传进来 -
子组件"提供菜单" :用
useImperativeHandle告诉父组件 :"你可以调用我的这几个方法" -
父组件"发号施令" : 通过
ref.current.方法名()直接调用
代码实现:
让我通过实现点击父组件内的一个按钮,使得子组件内的输入框自动聚焦,并获得子组件输入框内的值这一功能来体现该方法的作用


父子直接通信的使用场景:
例如:
- 在父组件里有一个按钮,点击后,想要直接命令子组件的输入框"自动聚焦",或者让子组件里的弹窗"打开" 此时使用
props便有些别扭 (此时父组件不需要给子组件传递数据,只是想"命令"它一下),此时便可使用Ref + useImperativeHandle,使父组件能够直接调用子组件内部的某些方法 - 控制DOM元素的行为:如聚焦,播放视频,滚动到某个位置
- 控制子组件的状态切换:如打开/关闭弹窗,展开/收起折叠面板
父子直接通信需要注意的点:
- 尽量不用,优先使用
props和回调函数 - 若不使用
useImperativeHandle则父组件能通过ref拿到子组件所有的东西,这是一个很危险的行为
结语
基于篇幅限制,本文需要让我们分为上中下上篇