📘 React 组件通信方式详解(含示例与优缺点)
本文将系统总结 React 项目中常见的组件通信方式,包括 props、Context、ref、事件总线、全局状态管理等,帮助你根据不同场景选择最合适的方式。
📌 1. 父子组件通信:props
父组件通过 props 向子组件传递数据或回调函数。
示例:
// 子组件 Child.tsx
const Child = ({ message, onReply }: { message: string; onReply: (text: string) => void }) => {
return (
<div>
<p>子组件收到:{message}</p>
<button onClick={() => onReply('你好,父组件!')}>回复</button>
</div>
);
};
// 父组件 Parent.tsx
const Parent = () => {
const handleReply = (msg: string) => {
console.log('收到子组件回复:', msg);
};
return <Child message="你好,子组件" onReply={handleReply} />;
};
✅ 优点:
- 简单直观,符合组件化思想
- 类型可控,利于维护
❌ 缺点:
- 只能用于父子或祖孙组件
- 多层传递时繁琐(props drilling)
📌 2. 兄弟组件通信:状态提升 + 共享父组件
兄弟组件无法直接通信,需要通过共同父组件中转。
示例:
// Parent.tsx
const Parent = () => {
const [data, setData] = useState('从A来');
return (
<>
<ComponentA send={(val) => setData(val)} />
<ComponentB msg={data} />
</>
);
};
const ComponentA = ({ send }: { send: (val: string) => void }) => {
return <button onClick={() => send('来自A的消息')}>发送</button>;
};
const ComponentB = ({ msg }: { msg: string }) => {
return <p>ComponentB收到:{msg}</p>;
};
✅ 优点:
- 数据集中在父组件,逻辑清晰
- 无需引入额外库
❌ 缺点:
- 耦合度高,父组件容易膨胀
- 不适合深层组件结构
📌 3. 跨层通信:Context API
用于共享全局数据,避免 props 层层传递。
示例:
// context.ts
export const ThemeContext = createContext('light');
// 父组件
const App = () => (
<ThemeContext.Provider value="dark">
<Child />
</ThemeContext.Provider>
);
// 子孙组件
const Child = () => {
const theme = useContext(ThemeContext);
return <p>当前主题:{theme}</p>;
};
✅ 优点:
- 解决 props drilling 问题
- 原生支持,无需第三方库
❌ 缺点:
- 不适合频繁变化的数据(如表单状态)
- 多个 Context 嵌套时难以管理
📌 4. 不相关组件通信:事件总线(Event Bus)
使用自定义事件系统(如 mitt)实现松耦合通信。
示例(基于 mitt):
npm install mitt
// eventBus.ts
import mitt from 'mitt';
export const bus = mitt();
// 发送组件 A.tsx
bus.emit('send-msg', '来自A的消息');
// 接收组件 B.tsx
useEffect(() => {
const handler = (msg: string) => console.log('B收到:', msg);
bus.on('send-msg', handler);
return () => bus.off('send-msg', handler);
}, []);
✅ 优点:
- 解耦、灵活,适合全局广播
- 非父子组件也能通信
❌ 缺点:
- 可读性差,不利于追踪数据流
- 易引起内存泄漏、调试困难
📌 5. 全局状态通信:Redux / Zustand / Recoil
用于大型项目中跨组件共享和管理复杂状态。
示例(Zustand):
npm install zustand
// store.ts
import { create } from 'zustand';
export const useUserStore = create((set) => ({
name: '张三',
setName: (name: string) => set({ name }),
}));
// A组件修改
useUserStore().setName('李四');
// B组件读取
const name = useUserStore().name;
示例(Redux ):
首先安装所需依赖:
npm install @reduxjs/toolkit react-redux
🧩 项目结构
src/
├── app/
│ └── store.ts ← Redux Store 配置
├── features/
│ └── counter/
│ ├── counterSlice.ts ← 状态切片
│ └── Counter.tsx ← 示例组件
├── App.tsx
└── main.tsx (或 index.tsx)
1️⃣ 创建 Store(store.ts)
// src/app/store.ts
import { configureStore } from '@reduxjs/toolkit';
import counterReducer from '../features/counter/counterSlice';
export const store = configureStore({
reducer: {
counter: counterReducer,
},
});
// 推导 RootState 和 AppDispatch 类型
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
2️⃣ 定义 Slice(counterSlice.ts)
// src/features/counter/counterSlice.ts
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
interface CounterState {
value: number;
}
const initialState: CounterState = {
value: 0,
};
const counterSlice = createSlice({
name: 'counter',
initialState,
reducers: {
increment: (state) => {
state.value += 1;
},
decrement: (state) => {
state.value -= 1;
},
incrementByAmount: (state, action: PayloadAction<number>) => {
state.value += action.payload;
},
},
});
export const { increment, decrement, incrementByAmount } = counterSlice.actions;
export default counterSlice.reducer;
3️⃣ 使用 Provider 包裹应用(main.tsx)
// src/main.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import { Provider } from 'react-redux';
import { store } from './app/store';
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
);
4️⃣ 在组件中使用 Redux(Counter.tsx)
// src/features/counter/Counter.tsx
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { RootState, AppDispatch } from '../../app/store';
import { increment, decrement, incrementByAmount } from './counterSlice';
const Counter = () => {
const count = useSelector((state: RootState) => state.counter.value);
const dispatch = useDispatch<AppDispatch>();
return (
<div>
<h2>计数器:{count}</h2>
<button onClick={() => dispatch(decrement())}>-1</button>
<button onClick={() => dispatch(increment())}>+1</button>
<button onClick={() => dispatch(incrementByAmount(5))}>+5</button>
</div>
);
};
export default Counter;
5️⃣ App 中使用组件(App.tsx)
// src/App.tsx
import React from 'react';
import Counter from './features/counter/Counter';
function App() {
return (
<div style={{ padding: '2rem' }}>
<h1>Redux Toolkit 示例</h1>
<Counter />
</div>
);
}
export default App;
✅ 输出结果
Redux Toolkit 示例
计数器:0
[ -1 ] [ +1 ] [ +5 ]
点击按钮即可修改 Redux 中的全局状态,并在组件中响应。
✅ 优点:
- 统一状态源,便于管理
- 可用于大型应用、多人协作
❌ 缺点:
- 学习成本略高
- 对小项目而言可能过重
✅ 总结表格
| 通信方式 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| props | 父子组件 | 简单直观、可控性强 | 层层传递繁琐 |
| 状态提升 | 兄弟组件 | 结构清晰 | 父组件易臃肿 |
| Context | 跨层级祖孙组件 | 解决深层传递问题 | 不适合频繁变化的状态 |
| 事件总线 | 不相关组件通信 | 解耦灵活 | 不可追踪,易出 bug |
| 全局状态管理 | 大型项目,复杂数据共享 | 状态集中、调试方便 | 引入成本高,小项目不推荐 |
🧠 写在最后
组件通信方式没有好坏,只有适不适合你的业务场景。小项目用 props + context 足够,大型项目配合 Zustand/Redux 更高效。
如果你有更优的实践,欢迎留言交流!
891

被折叠的 条评论
为什么被折叠?



