react - 组件之间的通信

文章目录

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

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

js 复制代码
// 典型场景示例
<App>
  <Header />
  <MainContent>
    <Sidebar onFilterChange={handleFilter} />
    <ProductList filter={currentFilter} />
  </MainContent>
  <Footer />
</App>

二、父子组件通信

1. Props 传值(父 → 子)

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

js 复制代码
// 父组件
function Parent() {
  const [message, setMessage] = useState("Hello from Parent");

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

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

传递函数的场景

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

js 复制代码
// 父组件
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 内容

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

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

三、子父组件通信

1. 回调函数(子 → 父)

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

js 复制代码
// 父组件
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 暴露子组件方法

js 复制代码
// 子组件
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)

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

js 复制代码
function Parent() {
  const [sharedState, setSharedState] = useState("");

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

2. 发布订阅模式

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

js 复制代码
// 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

跨层级组件共享状态

js 复制代码
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 运行时验证
js 复制代码
// 类型安全示例
interface ChildProps {
  message: string;
  onReply: (response: string) => void;
}

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

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


👉点击进入 我的网站

相关推荐
掘金安东尼5 小时前
纯 CSS 实现弹性文字效果
前端·css
牛奶5 小时前
Vue 基础理论 & API 使用
前端·vue.js·面试
牛奶6 小时前
Vue 底层原理 & 新特性
前端·vue.js·面试
anOnion6 小时前
构建无障碍组件之Radio group pattern
前端·html·交互设计
pe7er6 小时前
状态提升:前端开发中的状态管理的设计思想
前端·vue.js·react.js
SoaringHeart7 小时前
Flutter调试组件:打印任意组件尺寸位置信息 NRenderBox
前端·flutter
晚风予星8 小时前
Ant Design Token Lens 迎来了全面升级!支持在 .tsx 或 .ts 文件中直接使用 Design Token
前端·react.js·visual studio code
sunny_8 小时前
⚡️ vite-plugin-oxc:从 Babel 到 Oxc,我为 Vite 写了一个高性能编译插件
前端·webpack·架构
GIS之路8 小时前
ArcPy 开发环境搭建
前端
林小帅9 小时前
【笔记】OpenClaw 架构浅析
前端·agent