React 组件间通信全解:从 props 到全局状态的进阶指南

143 阅读4分钟

在 React 开发中,组件是构建用户界面的基本单元,而组件之间如何传递数据、协调行为,就是“组件通信”要解决的核心问题。

本文将系统梳理 React 中的组件通信方式,覆盖父子、兄弟、跨层级及全局通信的常见模式,并结合实际代码案例帮助你构建更清晰、健壮的组件架构。


🎯 为什么组件通信如此重要?

React 推崇单向数据流,数据从上往下传,事件从下往上传。小项目中,props 和回调足以满足通信需求;但随着项目规模变大,组件结构复杂、嵌套层级变深,“通信”的复杂度也会指数级上升。

掌握多种通信方式,可以让你:

  • 更灵活地组织组件层级
  • 更清晰地分离逻辑与展示
  • 更高效地维护大型项目的状态管理

🧱 1. 父子通信:props 与回调函数

这是最基础也是最推荐的通信方式,React 的单向数据流模型就是以它为核心。

✅ 父传子(props)

通过 props 将数据传递给子组件,子组件仅负责展示。

function Child({ message }) {
  return <p>{message}</p>;
}

function Parent() {
  return <Child message="Hello from Parent" />;
}

✅ 子传父(回调函数)

子组件调用父组件传入的函数,实现事件的上抛。

function Child({ onClick }) {
  return <button onClick={() => onClick("Clicked from Child")}>Click</button>;
}

function Parent() {
  const handleChildClick = (msg) => {
    console.log(msg);
  };

  return <Child onClick={handleChildClick} />;
}

📌 适用场景:结构清晰、数据流简单的父子组件。


🤝 2. 兄弟组件通信:通过共同父组件中转

兄弟组件之间无法直接通信,需要通过它们的共同父组件作为“中介”。

function SiblingA({ send }) {
  return <button onClick={() => send("Message from A")}>Send</button>;
}

function SiblingB({ message }) {
  return <p>Received: {message}</p>;
}

function Parent() {
  const [msg, setMsg] = useState("");

  return (
    <>
      <SiblingA send={setMsg} />
      <SiblingB message={msg} />
    </>
  );
}

📌 适用场景:兄弟组件间需要传递信息,且共享同一父组件。


🌐 3. 跨层级通信:使用 React Context

当子组件嵌套层级很深时,逐层通过 props 传递就会变得非常繁琐。这时候就可以使用 React.createContext 实现跨层数据共享。

✅ 创建上下文并提供数据:

jsx

const UserContext = createContext(null);

function App() {
  const user = { name: "Jiang", role: "admin" };

  return (
    <UserContext.Provider value={user}>
      <Layout />
    </UserContext.Provider>
  );
}

✅ 任意深层组件中消费:

function UserInfo() {
  const user = useContext(UserContext);
  return <p>当前用户:{user.name}</p>;
}

📌 适用场景:全局主题、用户信息、语言包、权限管理等。


🧩 4. 任意组件通信:使用全局状态管理工具

当通信对象之间没有父子关系,也没有共同祖先,或数据共享范围扩大到整个应用时,就需要使用专门的状态管理库

✅ Redux 示例(也可用 Zustand、Jotai 等)

// counterSlice.js
const initialState = { count: 0 };

function reducer(state = initialState, action) {
  if (action.type === "increment") return { count: state.count + 1 };
  return state;
}
// CounterA.jsx
const count = useSelector((state) => state.count);
// CounterB.jsx
const dispatch = useDispatch();
<button onClick={() => dispatch({ type: "increment" })}>+</button>;

📌 适用场景:跨模块、跨页面、跨组件的大型应用状态通信。


🔁 5. 轻量通信:事件总线(Event Bus)

对于不适合用 Redux 的项目中,可以自定义一个事件中心,在任意组件之间派发和监听事件。

// eventBus.js
const eventBus = {
  events: {},
  on(event, fn) {
    (this.events[event] ||= []).push(fn);
  },
  emit(event, data) {
    (this.events[event] || []).forEach(fn => fn(data));
  }
};

export default eventBus;

使用:

// A 组件
eventBus.emit("sendMsg", "Hello");

// B 组件
useEffect(() => {
  eventBus.on("sendMsg", (msg) => console.log(msg));
}, []);

📌 适用场景:页面间解耦、动态弹窗控制、独立组件通信等。


🔧 6. Hook 通信:自定义 Hook 共享逻辑状态

把共享逻辑封装到一个 Hook 中,在多个组件中调用这个 Hook 实现“间接通信”。

// useCounter.js
let count = 0;
const listeners = new Set();

export function useCounter() {
  const [value, setValue] = useState(count);

  useEffect(() => {
    listeners.add(setValue);
    return () => listeners.delete(setValue);
  }, []);

  const increment = () => {
    count += 1;
    listeners.forEach(fn => fn(count));
  };

  return { value, increment };
}

多个组件中使用后即可实时同步:

const { value, increment } = useCounter();

📌 适用场景:多个组件共享状态但不想引入全局库的轻量项目。


📊 通信方式对比总结

  • props / 回调:结构简单,推荐优先使用
  • context:解决 props drilling,适合共享配置
  • 状态管理库:适合大规模状态同步(Redux、Zustand 等)
  • 事件总线:组件完全解耦,适合临时广播通信
  • 自定义 hook:复用状态逻辑,适合中小型模块共享

🧠 最佳实践建议

  1. 优先使用 props 回调,保持数据流简洁可控
  2. 跨层嵌套多时用 Context,避免 props drilling
  3. 复杂状态共享用 Zustand/Redux,避免重复逻辑
  4. 轻耦合需求用事件总线/Hook 更灵活
  5. 保持通信“最小必要”,过度通信会导致维护困难

🧩 写在最后

组件间通信,是 React 架构设计中非常关键的一环。从最基础的 props,到跨层 Context,再到状态管理系统、事件机制,每一种通信方式都对应着不同的项目复杂度和场景需求。

掌握它们的原理与最佳实践,能让你的组件更清晰,项目结构更可控。