主题
组件通信
更新: 12/4/2025字数: 0 字 时长: 0 分钟
一、组件通信的意义与场景
在现代前端开发中,组件化架构将应用拆分为多个独立单元,而组件间的数据传递和交互成为关键。React 提供了多种通信方式适应不同场景需求:
jsx
// 典型场景示例
<App>
<Header />
<MainContent>
<Sidebar onFilterChange={handleFilter} />
<ProductList filter={currentFilter} />
</MainContent>
<Footer />
</App>二、父子组件通信
1. Props 传值(父 → 子)
最基础的通信方式,父组件通过 props 向子组件传递数据或函数
jsx
// 父组件
function Parent() {
const [message, setMessage] = useState("Hello from Parent");
return <Child greeting={message} />;
}
// 子组件
function Child({ greeting }) {
return <h1>{greeting}</h1>;
}传递函数的场景
如果子组件需要调用父组件的方法或更新状态,可将该方法作为 props 传递。下面是一个展开收起侧边栏的场景:
jsx
// 父组件
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 内容
jsx
// 父组件
function Card() {
return (
<div className="card">
<CardHeader>
<h2>自定义标题</h2>
</CardHeader>
</div>
);
}
// 子组件
function CardHeader({ children }) {
return <header className="card-header">{children}</header>;
}三、子父组件通信
1. 回调函数(子 → 父)
父组件传递函数给子组件,子组件调用该函数传值
jsx
// 父组件
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 暴露子组件方法
jsx
// 子组件
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)
将共享状态提升到最近的共同父组件
jsx
function Parent() {
const [sharedState, setSharedState] = useState("");
return (
<>
<SiblingA value={sharedState} onChange={setSharedState} />
<SiblingB value={sharedState} onClear={() => setSharedState("")} />
</>
);
}2. 发布订阅模式
通过事件总线实现解耦通信
jsx
// 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
跨层级组件共享状态
jsx
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) | 全局共享 | 增加复杂度 |
六、最佳实践建议
- 优先使用最简单的方案:能使用 props/回调就不使用复杂方案
- 避免过度使用 Context:仅用于真正全局的数据(如主题、用户信息)
- 性能优化:
- 对回调函数使用 useCallback
- 对传递值使用 useMemo
- 类型安全:
- TypeScript 提供 props 类型检查
- PropTypes 运行时验证
jsx
// 类型安全示例
interface ChildProps {
message: string;
onReply: (response: string) => void;
}
const Child: React.FC<ChildProps> = ({ message, onReply }) => {
// 组件实现
};没有最好的方案,只有最适合当前项目场景的方案。