前言
在 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 习惯的选择。