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

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


👉点击进入 我的网站

相关推荐
Lupino2 小时前
Node.js 与 Haskell 混合网络编程踩坑记:TCP 粘包与状态不一致引发的“死锁”
javascript·node.js
走粥2 小时前
JavaScript Promise
开发语言·前端·javascript
-CRzy2 小时前
CTF之web-信息收集
前端
神算大模型APi--天枢6462 小时前
合规落地加速期,大模型后端开发与部署的实战指南
大数据·前端·人工智能·架构·硬件架构
四瓣纸鹤2 小时前
F2图表柱状图添加文本标注
前端·javascript·antv/f2
inferno2 小时前
HTML基础(第二部分)
前端·html
Dreamcatcher_AC2 小时前
Ajax技术:前后端交互全解析
前端·ajax
韭菜炒大葱2 小时前
TailwindCSS:从“样式民工”到“UI乐高大师”的逆袭
前端·面试·编程语言
whyfail2 小时前
CSS实现水滴样式
前端·css