React 组件通信:组件间的 "悄悄话" 指南

在 React 开发中,组件是构成页面的基本单元,就像一个个独立的小个体。不同组件之间要协同工作,必然需要传递数据、互通消息 ------ 这就是组件通信。不同关系的组件(父子、兄弟、跨层级),通信方式各有讲究,我们今天就把这些 "悄悄话" 技巧讲明白。

一、父传子:长辈的 "零花钱"

核心逻辑 :这是 React 中最基础、最高频的通信方式,核心工具是 props。父组件作为数据的拥有者,像长辈给孩子零花钱一样,主动将数据通过 props 传递给子组件;子组件只能被动接收和使用,不能直接修改(React 单向数据流的核心体现)。

适用场景:所有父组件向直接嵌套的子组件传递数据的场景,比如传递展示文本、配置项、静态数据等。

父组件发钱现场

jsx 复制代码
// src/demo1/Parent.jsx

import Child from "./Child"

export default function Parent() {

const state = {

name: '小橘' // 准备给孩子的"零花钱"------要传递的核心数据

}

return (

<div>

<h2>父组件</h2>

{/* 把 state.name 包装成 msg 属性,通过 props 传递给 Child 组件 */}

{/* props 的属性名(msg)可自定义,只要子组件对应接收即可 */}

<Child msg={state.name}/> {/* 递钱ing */}

</div>

)

}

子组件收到钱的反应

jsx 复制代码
// src/demo1/Child.jsx

export default function Child(props) {

// 子组件的参数 props 是一个对象,包含了父组件传递的所有属性

console.log("收到钱了:", props); // 控制台打印:{msg: '小橘'},直观查看接收的数据

// 直接通过 props.属性名 使用父组件传递的数据

return <h3>子组件 -- 收到父组件消息:{props.msg}</h3>

}

关键说明

  • props 是只读的(immutable),子组件若想修改父组件的数据,不能直接改 props.msg,必须通过父组件提供的方法(后续子传父会讲);

  • 除了基本类型数据,props 还能传递函数、数组、对象甚至 JSX 元素,灵活性极高。

二、子传父:孩子向家里 "报账"

核心逻辑 :子组件没有直接修改父组件数据的权限,想传递自己的数据给父组件,需要借助 "回调函数"------ 父组件提前定义好接收数据的函数(相当于给孩子的 "报销单"),通过 props 传递给子组件;子组件在需要传递数据时(比如点击按钮、表单输入完成),调用这个回调函数并传入数据,父组件就能接收并处理。

适用场景:子组件触发交互后需同步数据到父组件,比如表单提交、按钮点击后的状态更新、子组件内部数据变化反馈等。

父组件发报销单

jsx 复制代码
// src/demo2/Parent.jsx

import Child from "./Child"

import { useState } from 'react'

export default function Parent() {

// 父组件用 useState 维护自己的状态(初始值为 1)

let [count, setCount] = useState(1)

// 定义回调函数:专门接收子组件传来的数据并处理

const getNum = (n) => {

// n 就是子组件传递过来的参数(报销金额)

setCount(n); // 用子组件的数据更新父组件的状态

}

return (

<div>

<h2>父组件二 -- 当前金额:{count}</h2>

{/* 把回调函数 getNum 通过 props 传给子组件,相当于递上报销单 */}

<Child getNum={getNum}/>

</div>

)

}

子组件填单报销

jsx 复制代码
// src/demo2/Child.jsx

export default function Child(props) {

const state = { num: 100 } // 子组件自己的内部数据------要报销的金额

// 定义发送函数:触发数据传递的逻辑

function send() {

// 调用父组件通过 props 传递的回调函数 getNum

// 把要传递的数据(state.num)作为参数传入,相当于提交报销单

props.getNum(state.num)

}

return (

<div>

<h3>子组件二</h3>

{/* 点击按钮触发 send 函数,完成数据传递 */}

<button onClick={send}>提交报销</button>

</div>

)

}

关键说明

  • 回调函数的本质是 "父组件把自己的方法传给子组件",子组件通过调用方法间接影响父组件,符合 React 单向数据流;

  • 传递的数据可以是任意类型,多个数据可封装成对象传入(比如 props.getNum({ num: 100, reason: '买文具' }))。

三、兄弟通信:借爸妈当 "传话筒"

核心逻辑:兄弟组件(同一父组件的直接子组件)之间没有直接的通信通道,必须借助它们的共同父组件作为 "传话筒"------ 这也是 React 官方推荐的 "状态提升" 方案。流程是:兄弟 A 先把数据传给父组件(子传父),父组件接收后更新自己的状态,再把状态传给兄弟 B(父传子),完成间接通信。

适用场景:平级组件需要共享数据或同步状态,比如一个开关组件和一个状态展示组件、输入框和搜索结果面板等。

爸妈当传话筒(父组件)

jsx 复制代码
// src/demo3/Parent.jsx

import { useState } from "react"

import Child1 from "./Child1"

import Child2 from "./Child2"

export default function Parent() {

// 父组件用 state 存储兄弟组件要共享的消息(初始值为 undefined)

let [message, setMessage] = useState()

// 回调函数:接收哥哥(Child1)传递的消息并更新状态

const getMsg = (msg) => {

setMessage(msg); // 把哥哥的消息存到父组件的状态里

}

return (

<div>

<h2> 父组件三 </h2>

{/* 给哥哥(Child1)传回调函数,用于接收它的消息 */}

<Child1 getMsg={getMsg}/> {/* 听哥哥说 */}

{/* 把存储的消息通过 props 传给弟弟(Child2) */}

<Child2 message={message}/> {/* 告诉弟弟 */}

</div>

)

}

哥哥组件(发消息方)

jsx 复制代码
// src/demo3/Child1.jsx

export default function Child1(props) {

// 哥哥要发送给弟弟的消息

const state = {

msg: '弟弟你好,我是哥哥!'

}

// 点击按钮触发发送:调用父组件的回调函数,把消息传给父组件

function send() {

props.getMsg(state.msg);

}

return (

<div>

<h3>子组件3.1(哥哥)</h3>

<button onClick={send}>给弟弟发消息</button>

</div>

)

}

弟弟组件(收消息方)

jsx 复制代码
// src/demo3/Child2.jsx

export default function Child2(props) {

// 直接通过 props 接收父组件转发的、来自哥哥的消息

// 用 || '暂无消息' 处理初始无消息的情况,避免页面显示 undefined

return (

<div>

<h3>子组件3.2(弟弟)</h3>

<p>收到哥哥的消息:{props.message || '暂无消息'}</p>

</div>

)

}

关键说明

  • 核心是 "状态提升"------ 把兄弟组件的共享状态抽到父组件管理,让父组件成为数据的唯一来源,保证数据流清晰;

  • 若兄弟组件层级较深(比如不是直接子组件),可结合 Context 或状态管理库,但简单场景下状态提升足够用。

四、跨级通信:家族 "微信群"

核心逻辑 :当组件层级很深(比如爷爷→爸爸→儿子→孙子),深层子组件想获取顶层组件的数据时,用 props 逐层传递会非常繁琐(俗称 "props 透传")。这时可以用 Context API 建立一个 "家族微信群":顶层组件作为 "群主" 提供数据,所有需要数据的组件(无论层级多深)都能直接 "看群消息",无需中间组件转发。

适用场景:多层嵌套组件共享全局数据,比如用户登录状态、主题设置、语言配置等。

建个家族群(顶层父组件)

jsx 复制代码
// src/demo4/Parent.jsx

import Child1 from "./Child1"

import { createContext } from 'react'

// 1. 创建 Context 对象:相当于新建一个"家族微信群",可设置默认值(可选)

export const Context = createContext()

export default function Parent() {

// 要在群里共享的数据------所有"群成员"都能访问

const parentData = '父组件的数据'

return (

<div>

<h2> 父组件四 </h2>

{/* 2. 用 Context.Provider 包装子组件树:相当于指定"群成员"范围 */}

{/* value 属性是要共享的数据:相当于在群里发消息 */}

<Context.Provider value={parentData}>

<Child1/> {/* Child1 及它的子组件都能访问 Context 数据 */}

</Context.Provider>

</div>

)

}

中间组件(无需转发消息)

jsx 复制代码
// src/demo4/Child1.jsx

import Child2 from "./Child2"

export default function Child1() {

// 中间组件不需要处理 Context 数据,也不用传递 props

// 直接渲染子组件即可,数据会"穿透"到深层组件

return (

<div>

<h3>子组件(中间层)</h3>

<Child2></Child2> {/* Child2 是深层子组件,能直接访问 Context */}

</div>

)

}

重孙看群消息(深层子组件)

jsx 复制代码
// src/demo4/Child2.jsx

import { useContext } from 'react'

import { Context } from './Parent'

export default function Child2() {

// 3. 用 useContext 钩子:相当于"查看群消息",直接获取 Context 中的数据

const msg = useContext(Context)

return <h4>孙子组件 --- 收到跨级消息:{msg}</h4>

}

关键说明

  • Context 不是 "全局状态管理",更适合 "局部跨层级共享",滥用会导致组件重渲染性能问题;

  • 若需要修改 Context 中的数据,可在顶层组件定义修改方法并一起放入 value,深层组件调用方法即可(比如 value={{ data: parentData, setData: setParentData }})。

总结:组件通信的 "核心原则"

React 组件通信的本质是 "数据的有序流动",不同场景对应不同方案,核心原则是 "简单优先、数据流清晰":

  • 父传子:用 props 直接传,简单直接;

  • 子传父:用回调函数,间接反馈;

  • 兄弟通信:状态提升到父组件,借父组件中转;

  • 跨级通信:用 Context API,避免 props 透传。

这些方案覆盖了大部分日常开发场景,若遇到大型应用、多组件共享复杂状态的情况,再考虑其他方法。记住:组件通信不需要追求 "高大上",能清晰、高效传递数据的方案就是好方案

相关推荐
爱上妖精的尾巴6 小时前
6-5 WPS JS宏 集合成员迭代(随机生成试题)
开发语言·前端·javascript
ycgg6 小时前
Webpack vs Vite 根本设计原理深度解析:为什么两者差异这么大?
前端
xrkhy6 小时前
canal1.1.8+mysql8.0+jdk17+rabbitMQ+redis的使用02
前端·redis·rabbitmq
Han.miracle7 小时前
HTML 核心基础与常用标签全解析
前端·html
几何心凉7 小时前
AI推理加速:openFuyao算力释放的核心引擎
前端
abcefg_h7 小时前
GO Web开发详细流程(无框架,restful风格,MVC架构)
开发语言·前端·golang
码界奇点7 小时前
基于Spring Cloud Alibaba与Vue.js的分布式在线教育系统设计与实现
前端·vue.js·分布式·spring cloud·架构·毕业设计·源代码管理
fruge7 小时前
Web Components 封装实战:打造可复用的跨框架组件
前端
糖墨夕7 小时前
超越随机:JavaScript中真正可靠的唯一标识符生成策略
前端·javascript