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

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


👉点击进入 我的网站

相关推荐
Wect29 分钟前
React 性能优化精讲
前端·react.js·性能优化
追风筝的人er1 小时前
SpringBoot+Vue3 企业考勤如何处理法定假期?节假日方案、调休补班与工作日判断链路拆解
前端·vue.js·后端
无敌的黑星星1 小时前
Java8 CompletableFuture 实战指南
linux·前端·python
雁鸣零落1 小时前
如何在 Chrome 中查看其他浏览器的书签?书签空间订阅与侧边栏只读切换指南
前端·chrome·edge浏览器
hpoenixf2 小时前
一天上线 + 零返工:我如何给复杂前端需求建立“安全感”
前端
广州华水科技3 小时前
单北斗GNSS变形监测系统在水利工程安全保障中的应用与优势分析
前端
yqcoder3 小时前
CSS 外边距重叠(Margin Collapsing):现象、原理与完美解决方案
前端·css
山楂树の4 小时前
图像标注大坑:img图片 + Canvas 叠加标注,同步放大后标注位置偏移、对不齐?详解修复方案及亚像素处理原理
前端·css·学习·canva可画
本山德彪4 小时前
我做了一个拼豆图纸生成器,把照片秒变图纸
前端
DTrader4 小时前
用TS无法实盘量化? - 实盘均线策略
前端·api