React组件间通信
一、父->子通信:Props
最基础也是最常用的方式。
- 父组件通过props向子组件传递数据或函数
- 子组件通过接受props来使用这些数据或调用函数
使用场景:组件层级清晰,数据只需要自上而下传递。
tsx
function Parent() {
const message = "hello from parent";
return <Child message={message}></Child>;
}
function Child(props: { message: string }) {
return <p>{props.message}</p>;
}
二、子->父组件:回调函数
React中数据是单向流动的,因此子组件如需向父组件传递数据,需依赖父组件提供的回调。
- 父组件定义回调函数并通过props传给子组件
- 子组件调用该回调,将数据"抛回"给父组件
适用场景: 用户操作需要让父组件更新数据,例如点击事件、输入框数据回传等。
tsx
function Parent() {
const handleChildData = (data: string) => {
console.log("data from child:", data);
};
return <Child onSendData={handleChildData}></Child>;
}
function Child({ onSendData }: { onSendData: (data: string) => void }) {
return (
<button onClick={() => onSendData("hello from 'child")}>send data</button>
);
}
三、兄弟组件通信
兄弟组件之间无法直接通信,一般通过提升state到最近的共同父组件实现:
- 将共享数据提升到共同父组件
- 父组件通过props向两个子组件传递数据和修改方法
适用场景:界面中两个兄弟组件需要共享状态或相互影响。
tsx
function SiblingA({data}:{data:string}){
return (
<p>{data}</p>
)
}
function SiblingB({onUpdateData}:{onUpdateData:(data: string)=> void}){
return (
<button onClick={()=>onUpdateData('123')}>update data</button>
)
}
function Parent() {
const [sharedData, setSharedData] = useState('Initial Data')
return (
<div>
<SiblingA data={sharedData}></SiblingA>
<SiblingB onUpdateData={(val)=>setSharedData(val)}></SiblingB>
</div>
)
}
四、跨层通信
当层级较深、props传递链条过长时,可以使用Context。
- 提供方(Provider)在高层包裹组件树
- 消费方(useContext)在任意组件中访问数据
适用场景: 主题、语言、多层共享状态等需要跨层访问的数据。
tsx
const ThemeDefault = "light";
const ThemeContext = createContext(ThemeDefault);
const Context = () => {
const [theme, setTheme] = useState(ThemeDefault);
function handleTheme() {
if (theme === "light") setTheme("dark");
else setTheme("light");
}
return (
<div>
<h2>跨层通信</h2>
<button onClick={handleTheme}>切换主题</button>
<hr />
<ThemeContext.Provider value={theme}>
<ToolBar />
</ThemeContext.Provider>
</div>
);
};
function ToolBar() {
return <ThemeButton></ThemeButton>;
}
function ThemeButton() {
const theme = useContext(ThemeContext);
return (
<button style={{ background: theme === "dark" ? "#333" : "#eee" }}>
按钮
</button>
);
}
五、全局状态
当项目状态复杂时,可以使用全局状态管理工具(如Zustand、Redux、Jotai、Recoil)。
优点:
- 数据全局可访问、可响应式更新
- 跨页面、跨组件共享
- 对大型项目尤为重要
适用场景:
- 需要在多处使用的全局状态
- 数据修改后需要触发多组件联动渲染
tsx
const useStore = create((set) => ({
count: 0,
increment: ()=> set((state: { count: number }) => ({count: state.count + 1}))
}))
function Counter() {
const {count, increment} = useStore() as {count:number,increment:()=>void}
return (
<div>
<p>{count}</p>
<button onClick={increment}>加</button>
</div>
)
}
六、事件总线
通过事件实例(如mitt或自定义emitter)实现任意组件间通信:
emitter.emit('event', data)发送消息emitter.on('event', handler)监听消息
优点:
- 无组件层级限制
- 松耦合、灵活
适用场景:
- 页面内独立组件之间的通知
- 轻量级的跨组件事件触达
tsx
// A组件中触发事件
function ComponentA() {
return (
<button onClick={() => emitter.emit("message", "A: hello component B")}>
触发事件:向B组件传递消息
</button>
);
}
// B组件中接收消息
function ComponentB() {
const [msg, setMsg] = useState("waiting msg");
// 避免重复注册监听事件
useEffect(() => {
const hanlderEvent = (message: string) => {
console.log(message);
setMsg(message);
};
emitter.on("message", hanlderEvent);
return () => {
// 卸载组件时移除
emitter.off("message", hanlderEvent);
};
}, []);
return <p>{msg}</p>;
}
总结
| 通信方式 | 特点 | 场景 |
|---|---|---|
| Props(父->子) | 最基础、最直观 | 单向向下传递数据 |
| 回调(子->父) | 子组件向父组件"汇报" | 表单、操作事件回传 |
| 状态提升(兄弟通信) | 通过共同父组件协调 | 兄弟组件共享状态 |
| Context | 解决props层层传递 | 多层级共享数据 |
| 全局状态 | 跨页面、跨业务共享 | 中大型项目 |
| 事件总线 | 任意组件互相通信 | 异步事件、低耦合通信 |