Skip to content
 

组件通信

更新: 12/4/2025字数: 0 字 时长: 0 分钟

一、组件通信的意义与场景

在现代前端开发中,组件化架构将应用拆分为多个独立单元,而组件间的数据传递和交互成为关键。React 提供了多种通信方式适应不同场景需求:

jsx
// 典型场景示例
<App>
  <Header />
  <MainContent>
    <Sidebar onFilterChange={handleFilter} />
    <ProductList filter={currentFilter} />
  </MainContent>
  <Footer />
</App>

二、父子组件通信

1. Props 传值(父 → 子)

最基础的通信方式,父组件通过 props 向子组件传递数据或函数

jsx
// 父组件
function Parent() {
  const [message, setMessage] = useState("Hello from Parent");

  return <Child greeting={message} />;
}

// 子组件
function Child({ greeting }) {
  return <h1>{greeting}</h1>;
}

传递函数的场景

如果子组件需要调用父组件的方法或更新状态,可将该方法作为 props 传递。下面是一个展开收起侧边栏的场景:

jsx
// 父组件
function Parent() {
  const [sidebarOpen, setSidebarOpen] = useState(true);

  return <MyAppInfo onIconButtonClick={() => setSidebarOpen(!sidebarOpen)} />;
}

// 子组件
function Child({ onIconButtonClick }) {
  <button onClick={onIconButtonClick}>图标按钮</button>;
}

因为 setSidebarOpen 函数是在父组件中定义的,所以子组件可以调用它来更新父组件的状态。

2. Children 插槽(父 → 子)

通过 props.children 传递 JSX 内容

jsx
// 父组件
function Card() {
  return (
    <div className="card">
      <CardHeader>
        <h2>自定义标题</h2>
      </CardHeader>
    </div>
  );
}

// 子组件
function CardHeader({ children }) {
  return <header className="card-header">{children}</header>;
}

三、子父组件通信

1. 回调函数(子 → 父)

父组件传递函数给子组件,子组件调用该函数传值

jsx
// 父组件
function Parent() {
  const handleChildEvent = (data) => {
    console.log("来自子组件的数据:", data);
  };

  return <Child onEvent={handleChildEvent} />;
}

// 子组件
function Child({ onEvent }) {
  const sendData = () => {
    onEvent({ value: "子组件数据" });
  };

  return <button onClick={sendData}>发送数据</button>;
}

2. useImperativeHandle(子 → 父)

通过 ref 暴露子组件方法

jsx
// 子组件
const Child = forwardRef((props, ref) => {
  const [count, setCount] = useState(0);

  useImperativeHandle(ref, () => ({
    getCount: () => count,
    reset: () => setCount(0)
  }));

  return <button onClick={() => setCount((c) => c + 1)}>点击 {count}</button>;
});

// 父组件
function Parent() {
  const childRef = useRef();

  const logCount = () => {
    console.log("当前计数:", childRef.current?.getCount());
  };

  return (
    <>
      <Child ref={childRef} />
      <button onClick={logCount}>获取子组件状态</button>
    </>
  );
}

四、兄弟组件通信

1. 状态提升(Lifting State Up)

将共享状态提升到最近的共同父组件

jsx
function Parent() {
  const [sharedState, setSharedState] = useState("");

  return (
    <>
      <SiblingA value={sharedState} onChange={setSharedState} />
      <SiblingB value={sharedState} onClear={() => setSharedState("")} />
    </>
  );
}

2. 发布订阅模式

通过事件总线实现解耦通信

jsx
// eventBus.js
const EventBus = {
  events: {},
  emit(event, data) {
    if (!this.events[event]) return;
    this.events[event].forEach((cb) => cb(data));
  },
  on(event, callback) {
    if (!this.events[event]) this.events[event] = [];
    this.events[event].push(callback);
  }
};

// 组件A
function ComponentA() {
  const handleClick = () => {
    EventBus.emit("dataUpdate", { value: "来自A的数据" });
  };

  return <button onClick={handleClick}>发送数据</button>;
}

// 组件B
function ComponentB() {
  const [data, setData] = useState("");

  useEffect(() => {
    EventBus.on("dataUpdate", setData);
    return () => EventBus.off("dataUpdate", setData);
  }, []);

  return <div>接收到的数据: {data}</div>;
}

3. Context API

跨层级组件共享状态

jsx
const DataContext = createContext();

function Parent() {
  const [sharedData, setSharedData] = useState("");

  return (
    <DataContext.Provider value={{ sharedData, setSharedData }}>
      <SiblingA />
      <SiblingB />
    </DataContext.Provider>
  );
}

function SiblingA() {
  const { setSharedData } = useContext(DataContext);

  return <input onChange={(e) => setSharedData(e.target.value)} />;
}

function SiblingB() {
  const { sharedData } = useContext(DataContext);

  return <div>输入内容: {sharedData}</div>;
}

五、通信方案选型指南

场景推荐方案优点缺点
简单父子Props 传递简单直接多层传递繁琐
子父交互回调函数直观可控回调地狱风险
兄弟组件状态提升官方推荐需中间组件
跨层级Context避免 prop drilling可能引起不必要渲染
完全解耦状态管理(Redux)全局共享增加复杂度

六、最佳实践建议

  1. 优先使用最简单的方案:能使用 props/回调就不使用复杂方案
  2. 避免过度使用 Context:仅用于真正全局的数据(如主题、用户信息)
  3. 性能优化
    • 对回调函数使用 useCallback
    • 对传递值使用 useMemo
  4. 类型安全
    • TypeScript 提供 props 类型检查
    • PropTypes 运行时验证
jsx
// 类型安全示例
interface ChildProps {
  message: string;
  onReply: (response: string) => void;
}

const Child: React.FC<ChildProps> = ({ message, onReply }) => {
  // 组件实现
};

没有最好的方案,只有最适合当前项目场景的方案。

我见青山多妩媚,料青山见我应如是。