React-React 组件通信的 6 种绝佳实践

100 阅读2分钟

前言

在 React 开发中,组件通信是构建复杂应用的基础。根据组件间的关系(父子、跨层级、兄弟),我们需要选择最合适的通信方案。本文将深度解析常见的通信模式。

一、 父传子:Props 属性传递

这是最基础的通信方式,数据通过 Props 单向流动

import React from 'react';

interface ChildProps {
  title: string;
  count: number;
}

const Child: React.FC<ChildProps> = ({ title, count }) => {
  return (
    <div className="child">
      <h3>子组件:{title}</h3>
      <p>接收到的计数:{count}</p>
    </div>
  );
};

export const Parent: React.FC = () => {
  return <Child title="我是父组件传来的" count={100} />;
};

二、 父组件调用子组件方法:forwardRef + useImperativeHandle

有时父组件需要主动触发子组件的行为(如重置表单、开启弹窗)。由于函数组件没有实例,必须使用 forwardRef 暴露接口。

import React, { useRef, useImperativeHandle, forwardRef } from 'react';

// 1. 定义子组件暴露出来的接口类型
export interface ChildRef {
  reset: () => void;
  sayHello: () => void;
}

// 2. 使用 forwardRef 包裹子组件
const Child = forwardRef<ChildRef, {}>((props, ref) => {
  useImperativeHandle(ref, () => ({
    reset: () => console.log('子组件状态已重置'),
    sayHello: () => alert('Hello from Child!')
  }));

  return <div className="child">我是子组件</div>;
});

// 3. 父组件调用
export const Father: React.FC = () => {
  const childRef = useRef<ChildRef>(null);

  return (
    <>
      <Child ref={childRef} />
      <button onClick={() => childRef.current?.sayHello()}>调用子组件方法</button>
    </>
  );
};

三、 子传父:回调函数

子组件通过调用父组件传递的函数,实现反向传值。

interface ChildProps {
  onSendMessage: (msg: string) => void;
}

const Child: React.FC<ChildProps> = ({ onSendMessage }) => {
  return <button onClick={() => onSendMessage('Hello Father!')}>传值给父组件</button>;
};

export const Parent: React.FC = () => {
  const handleMsg = (msg: string) => console.log('收到子组件消息:', msg);
  return <Child onSendMessage={handleMsg} />;
};

四、 跨层级通信:Context + useReducer

当嵌套过深时,Props 传递会变得复杂。此时可以使用 useContext 实现状态共享。

import React, { createContext, useContext, useReducer, ReactNode } from 'react';

// 1. 定义状态类型
interface State { count: number }
type Action = { type: 'add' } | { type: 'sub' };

// 2. 创建上下文
const CountContext = createContext<{ state: State; dispatch: React.Dispatch<Action> } | null>(null);

// 3. Provider 组件封装
export const CountProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
  const [state, dispatch] = useReducer((s: State, a: Action) => {
    if (a.type === 'add') return { count: s.count + 1 };
    return s;
  }, { count: 0 });

  return <CountContext.Provider value={{ state, dispatch }}>{children}</CountContext.Provider>;
};

// 4. 深层子组件使用
export const GrandChild: React.FC = () => {
  const context = useContext(CountContext);
  return <button onClick={() => context?.dispatch({ type: 'add' })}>累加:{context?.state.count}</button>;
};

五、 事件总线:EventBus (以 mitt 为例)

适用于完全没有嵌套关系的兄弟组件。通过发布/订阅模式解耦。

import mitt from 'mitt';

// 定义事件类型
type Events = {
  foo: string;
};

export const emitter = mitt<Events>();

// 组件 A:订阅
export const ComponentA: React.FC = () => {
  React.useEffect(() => {
    emitter.on('foo', (msg) => console.log('监听到事件:', msg));
    return () => emitter.off('foo'); // 务必在销毁时解绑
  }, []);
  return <div>组件 A</div>;
};

// 组件 B:发布
export const ComponentB: React.FC = () => {
  return <button onClick={() => emitter.emit('foo', '来自 B 的信号')}>发送信号</button>;
};

六、 状态管理库:Redux / MobX / Zustand

当应用规模巨大,业务逻辑复杂时,推荐使用成熟的状态管理库。

  • Redux / Toolkit:流程严谨,适合大型多人协作项目。
  • MobX:响应式监听,代码量少。
  • Zustand:目前最轻量、最符合 Hooks 习惯的选择。