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 }) => {
  // 组件实现
};

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


👉点击进入 我的网站

相关推荐
alexander06815 小时前
CSS 类选择器组合
前端·css
寅时码20 小时前
React 之死·终章:一个 useRef,把闭包陷阱、依赖数组、漫天 rerender 全送走
前端·react.js·ai编程
不好听61320 小时前
HTML 事件监听机制:从 DOM Level 0 到 React 合成事件
前端·react.js·html
wordbaby20 小时前
为什么刷新页面就 404?聊聊 SPA 路由与 Nginx 的那点事
前端
daols8820 小时前
vxe-table 渲染器教程一:实现金额输入控件
javascript·vue.js·vxe-table
不好听61321 小时前
React 核心概念:JSX、组件与数据驱动
前端·react.js
触底反弹21 小时前
🔥 React 零基础入门(中):500 行屎山代码到组件化的蜕变
前端·javascript·react.js
不好听61321 小时前
React vs Vue:两大前端框架技术选型深度对比
前端·vue.js·react.js
GuWenyue21 小时前
Cursor黑盒拆解!1套LangChain.js手写Mini编程Agent,自动生成React项目,效率提升60%
前端·数据库·人工智能
GuWenyue21 小时前
传统Agent工具两大痛点!300行代码落地MCP跨语言工具,彻底解耦LLM与工具
前端·人工智能·算法