This repository has been archived on 2026-05-24. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
all-in-kingsoft/hhs/REACT/4. 进阶篇/11-组件通信模式.md
T
2026-04-29 22:19:51 +08:00

188 lines
4.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
tags: [React, Component Communication, Props, Context, Frontend]
create time: 2026-04-29 22:10
---
# 组件通信模式
## 概述
React 中父子组件之间的数据流动有多种方式。理解每种模式的适用场景和性能影响,能够避免过度设计或通信瓶颈。本文档从简单到复杂,梳理完整的通信方案谱系。
## 通信方向全景图
```mermaid
graph TD
A["父 → 子"] --> B["Props(最基础)"]
A --> C["Context Provider(跨层级)"]
A --> D["状态管理库(全局共享)"]
E["子 → 父"] --> F["回调 Prop(事件驱动)"]
E --> G["refs(imperative)"]
H["兄弟组件"] --> I["状态提升到共同父组件"]
H --> J["通过共同祖先通信"]
H --> K["状态管理库 / 发布订阅"]
style B fill:#4FC08D,color:#fff
style C fill:#F5A87D,color:#000
style F fill:#61DAFB,color:#000
```
## Props Drill(属性逐层透传)
### 问题场景
```tsx
function App() {
const user = getUser();
return <Header user={user} />; // Header 不需要 user,但深层的 Avatar 需要!
}
function Header({ user }) {
return (
<nav>
<Logo /> {/* ❌ 无需 user,却接收了 */}
<Avatar user={user} /> {/* ✅ 需要 */}
</nav>
);
}
```
### 解决方案对比
| 方法 | 适合场景 | 额外依赖 |
|------|----------|---------|
| **Context** | 少量深层传递(主题、用户信息) | 无 |
| **自定义 Hook + Props 重组织** | 中等深度(3-4 层) | 无 |
| **状态管理库** | 频繁变化、多组件共享 | Zustand/Redux |
| **Render Prop / HOC** | 复用逻辑而非传值 | 无 |
## Callback Prop(子 → 父)
```tsx
interface FormProps {
onSubmit: (data: FormValues) => Promise<void>;
}
function LoginForm({ onSubmit }: FormProps) {
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
const data = collectFormData();
onSubmit(data); // 通知父组件
};
return <form onSubmit={handleSubmit}><Button type="submit">登录</Button></form>;
}
// 父组件
function App() {
const handleLogin = async (data: FormValues) => {
await api.login(data);
navigate("/dashboard");
};
return <LoginForm onSubmit={handleLogin} />;
}
```
> [!note] 为什么不用 props.children 做通信?
> children 是 UI 内容插槽,不是通信机制。通信用 callback prop 保持关注点分离。
## Context 跨层级通信
```tsx
const UserContext = createContext<User | null>(null);
function UserProfile({ children }: { children: React.ReactNode }) {
const user = useDatabaseUser(userId);
return (
<UserContext.Provider value={user}>
{/* Avatar、Settings、OrderHistory 任意深度都可消费 */}
{children}
</UserContext.Provider>
);
}
function OrderHistory() {
const user = useContext(UserContext); // 直接获取,无需中间层透传
return <div>{user?.orders?.map(...)}</div>;
}
```
> [!warning] Context 的性能陷阱
> - Provider value 对象每次渲染都是新引用 → 所有消费者重渲染
> - 解决:拆分 Context、或使用 `useReducer` 返回稳定的 `{state, dispatch}` 对象
## 状态管理库方案
```tsx
// Zustand 方案:任意两个组件共享 state,无需祖先后代关系
import { create } from "zustand";
const useCartStore = create((set) => ({
items: [],
addItem: (item) => set(state => ({ items: [...state.items, item] })),
}));
function ProductCard() {
const addItem = useCartStore(s => s.addItem);
return <button onClick={() => addItem(product)}>加入购物车</button>;
}
function CartIcon() {
const items = useCartStore(s => s.items);
return <Badge>{items.length}</Badge>;
}
// 两者毫无关联,但共享同一份状态
```
## Ref Imperative API(命令式通信)
```tsx
const formRef = useRef<FormHandle>(null);
function Parent() {
const handleSubmit = () => {
formRef.current?.validate(); // 命令子组件执行方法
formRef.current?.reset();
};
return <><ChildForm ref={formRef} /><button onClick={handleSubmit}>提交</button></>;
}
const ChildForm = forwardRef<FormHandle>((props, ref) => {
useImperativeHandle(ref, () => ({
validate: () => validator.validate(),
reset: () => setFields(initialValues),
}));
return <form>...</form>;
});
```
## 决策树
```mermaid
graph TD
A["需要通信?"] -->|"是"| B["通信范围?"]
B --> C["仅父子两层"]
B --> D["多层级穿透"]
B --> E["跨层级 + 兄弟之间"]
B --> F["需命令式调用子组件方法"]
C --> G["Props + Callback ✅"]
D --> H["Context ✅"]
E --> I["Zustand / Redux ✅"]
F --> J["forwardRef + useImperativeHandle ✅"]
style G fill:#4FC08D,color:#fff
style H fill:#61DAFB,color:#000
style I fill:#F5A87D,color:#000
style J fill:#A0AEC0,color:#000
```
## 关联笔记