![]()
组件通信大全
一、组件通信概述
1.1 通信场景分类
| 通信类型 | 场景 | 常用方式 |
|---|
| 父子通信 | 父→子 | Props |
| 父子通信 | 子→父 | 回调函数 |
| 兄弟通信 | 同级组件 | 状态提升 |
| 跨层级通信 | 祖先后代 | Context、Redux |
| 全局通信 | 任意组件 | 状态管理库 |
二、父子组件通信
2.1 父传子(Props)
// 父组件 function Parent() { const [message, setMessage] = useState('来自父组件的消息'); return ( <div> <Child message={message} /> </div> ); } // 子组件 function Child({ message }) { return <div>{message}</div>; }
2.2 子传父(回调函数)
function Parent() { const [data, setData] = useState(''); const handleChildData = (childData) => { setData(childData); }; return ( <div> <Child onSendData={handleChildData} /> <p>子组件传来的数据: {data}</p> </div> ); } function Child({ onSendData }) { const sendData = () => { onSendData('这是子组件的数据'); }; return <button onClick={sendData}>发送数据给父组件</button>; }
2.3 父组件调用子组件方法
const Child = forwardRef((props, ref) => { const [value, setValue] = useState(''); useImperativeHandle(ref, () => ({ reset: () => setValue(''), getValue: () => value, focus: () => inputRef.current.focus() })); return <input value={value} onChange={(e) => setValue(e.target.value)} />; }); function Parent() { const childRef = useRef(null); return ( <div> <Child ref={childRef} /> <button onClick={() => childRef.current.reset()}>重置</button> </div> ); }
三、兄弟组件通信
3.1 状态提升
function Parent() { const [sharedState, setSharedState] = useState(''); return ( <div> <SiblingA onUpdate={setSharedState} /> <SiblingB sharedState={sharedState} /> </div> ); } function SiblingA({ onUpdate }) { return <button onClick={() => onUpdate('来自兄弟A的消息')}>发送消息</button>; } function SiblingB({ sharedState }) { return <div>收到消息: {sharedState}</div>; }
3.2 通过父组件中转
function Parent() { const [count, setCount] = useState(0); const increment = () => setCount(count + 1); const decrement = () => setCount(count - 1); return ( <div> <CounterDisplay count={count} /> <CounterControls onIncrement={increment} onDecrement={decrement} /> </div> ); } function CounterDisplay({ count }) { return <h1>{count}</h1>; } function CounterControls({ onIncrement, onDecrement }) { return ( <div> <button onClick={onIncrement}>+</button> <button onClick={onDecrement}>-</button> </div> ); }
四、跨层级通信
4.1 Context API 基础
// 创建 Context const ThemeContext = React.createContext('light'); // 提供者 function ThemeProvider({ children }) { const [theme, setTheme] = useState('light'); const toggleTheme = () => { setTheme(prev => prev === 'light' ? 'dark' : 'light'); }; return ( <ThemeContext.Provider value={{ theme, toggleTheme }}> {children} </ThemeContext.Provider> ); } // 消费者(函数组件) function ThemedButton() { const { theme, toggleTheme } = useContext(ThemeContext); return ( <button onClick={toggleTheme} style={{ background: theme === 'dark' ? '#333' : '#fff' }}> 当前主题: {theme} </button> ); } // 使用 function App() { return ( <ThemeProvider> <ThemedButton /> </ThemeProvider> ); }
4.2 Context + useReducer
const AppContext = React.createContext(); const initialState = { user: null, theme: 'light', notifications: [] }; function reducer(state, action) { switch (action.type) { case 'SET_USER': return { ...state, user: action.payload }; case 'TOGGLE_THEME': return { ...state, theme: state.theme === 'light' ? 'dark' : 'light' }; case 'ADD_NOTIFICATION': return { ...state, notifications: [...state.notifications, action.payload] }; default: return state; } } function AppProvider({ children }) { const [state, dispatch] = useReducer(reducer, initialState); return ( <AppContext.Provider value={{ state, dispatch }}> {children} </AppContext.Provider> ); } // 在任何组件中使用 function UserProfile() { const { state, dispatch } = useContext(AppContext); const login = () => { dispatch({ type: 'SET_USER', payload: { name: '张三', id: 1 } }); }; return ( <div> {state.user ? <div>欢迎 {state.user.name}</div> : <button onClick={login}>登录</button>} </div> ); }
4.3 嵌套 Context
const UserContext = React.createContext(); const ThemeContext = React.createContext(); const LanguageContext = React.createContext(); function App() { return ( <UserContext.Provider value={{ user: '张三' }}> <ThemeContext.Provider value={{ theme: 'dark' }}> <LanguageContext.Provider value={{ lang: 'zh-CN' }}> <Dashboard /> </LanguageContext.Provider> </ThemeContext.Provider> </UserContext.Provider> ); } function Dashboard() { const user = useContext(UserContext); const theme = useContext(ThemeContext); const language = useContext(LanguageContext); return <div>{user.user} - {theme.theme} - {language.lang}</div>; }
五、使用状态管理库
5.1 Zustand 示例
npminstallzustand
import create from 'zustand'; const useStore = create((set) => ({ count: 0, user: null, increment: () => set((state) => ({ count: state.count + 1 })), decrement: () => set((state) => ({ count: state.count - 1 })), setUser: (user) => set({ user }) })); // 组件 A function Counter() { const { count, increment, decrement } = useStore(); return ( <div> <button onClick={decrement}>-</button> <span>{count}</span> <button onClick={increment}>+</button> </div> ); } // 组件 B(自动响应状态变化) function Display() { const count = useStore((state) => state.count); return <div>当前计数: {count}</div>; }
5.2 Redux Toolkit 示例
npminstall@reduxjs/toolkit react-redux
import { configureStore, createSlice } from '@reduxjs/toolkit'; import { Provider, useSelector, useDispatch } from 'react-redux'; const counterSlice = createSlice({ name: 'counter', initialState: { value: 0 }, reducers: { increment: (state) => { state.value += 1; }, decrement: (state) => { state.value -= 1; } } }); const store = configureStore({ reducer: counterSlice.reducer }); function Counter() { const count = useSelector((state) => state.value); const dispatch = useDispatch(); return ( <div> <button onClick={() => dispatch(counterSlice.actions.decrement())}>-</button> <span>{count}</span> <button onClick={() => dispatch(counterSlice.actions.increment())}>+</button> </div> ); } function App() { return ( <Provider store={store}> <Counter /> </Provider> ); }
六、事件总线(不推荐)
// 简单的事件总线实现(用于理解概念) class EventBus { constructor() { this.events = {}; } on(event, callback) { if (!this.events[event]) this.events[event] = []; this.events[event].push(callback); } emit(event, data) { if (this.events[event]) { this.events[event].forEach(callback => callback(data)); } } off(event, callback) { if (this.events[event]) { this.events[event] = this.events[event].filter(cb => cb !== callback); } } } const eventBus = new EventBus(); // 组件 A function Sender() { const sendMessage = () => { eventBus.emit('message', '来自发送者的消息'); }; return <button onClick={sendMessage}>发送</button>; } // 组件 B function Receiver() { const [message, setMessage] = useState(''); useEffect(() => { const handler = (msg) => setMessage(msg); eventBus.on('message', handler); return () => eventBus.off('message', handler); }, []); return <div>收到: {message}</div>; }
七、通信模式对比
| 模式 | 优点 | 缺点 | 适用场景 |
|---|
| Props | 简单、明确 | 逐层传递繁琐 | 浅层组件树 |
| Context | 跨层级传递 | 过度使用影响性能 | 主题、语言、用户信息 |
| Zustand | 轻量、简单 | 生态相对小 | 中小型应用 |
| Redux | 成熟、生态丰富 | 样板代码多 | 大型复杂应用 |
| 事件总线 | 灵活 | 难以追踪、维护困难 | 不推荐使用 |
八、最佳实践
8.1 选择合适的通信方式
// 简单场景:Props function Simple() { return <Child name="张三" />; } // 深层传递:Context const UserContext = createContext(); // 复杂状态:Zustand/Redux const useStore = create(...);
8.2 避免 Props Drilling
// ❌ 不好的做法:层层传递 <GrandParent user={user}> <Parent user={user}> <Child user={user}> <GrandChild user={user} /> </Child> </Parent> </GrandParent> // ✅ 好的做法:使用 Context const UserContext = createContext(); <UserContext.Provider value={user}> <GrandParent> <Parent> <Child> <GrandChild /> </Child> </Parent> </GrandParent> </UserContext.Provider>
8.3 组件设计原则
// 容器组件(负责逻辑) function UserContainer() { const [user, setUser] = useState(null); const [loading, setLoading] = useState(true); useEffect(() => { fetchUser().then(data => { setUser(data); setLoading(false); }); }, []); return <UserProfile user={user} loading={loading} />; } // 展示组件(负责 UI) function UserProfile({ user, loading }) { if (loading) return <div>加载中...</div>; return <div>{user.name}</div>; }
九、常见问题
9.1 Context 性能优化
// 拆分 Context 避免不必要的重渲染 const UserContext = createContext(); const ThemeContext = createContext(); // 使用 useMemo 优化 Provider 值 function AppProvider({ children }) { const [user, setUser] = useState(null); const userValue = useMemo(() => ({ user, setUser }), [user]); return ( <UserContext.Provider value={userValue}> {children} </UserContext.Provider> ); }
9.2 循环依赖
// 避免组件之间相互导入导致的循环依赖 // 解决方案:提取共享逻辑到单独文件 // shared/types.js export const SHARED_CONSTANTS = {}; // 或使用 Context 解耦
十、练习题
基础题
- 实现一个简单的计数器,使用兄弟组件通信(一个组件显示,一个组件控制)
- 实现一个主题切换功能,使用 Context
进阶题
- 实现一个购物车,使用 Zustand 管理状态
- 实现一个通知系统,任意组件可以发送通知
参考答案
// 购物车示例(Zustand) const useCartStore = create((set) => ({ items: [], addItem: (item) => set((state) => ({ items: [...state.items, item] })), removeItem: (id) => set((state) => ({ items: state.items.filter(item => item.id !== id) })), clearCart: () => set({ items: [] }), total: () => { const state = useCartStore.getState(); return state.items.reduce((sum, item) => sum + item.price * item.quantity, 0); } })); function ProductList() { const addItem = useCartStore(state => state.addItem); return ( <button onClick={() => addItem({ id: 1, name: '商品', price: 100, quantity: 1 })}> 添加到购物车 </button> ); } function Cart() { const items = useCartStore(state => state.items); const removeItem = useCartStore(state => state.removeItem); const total = useCartStore(state => state.total()); return ( <div> {items.map(item => ( <div key={item.id}> {item.name} - ¥{item.price} <button onClick={() => removeItem(item.id)}>删除</button> </div> ))} <div>总计: ¥{total}</div> </div> ); }
十一、小结
| 通信类型 | 推荐方案 | 备注 |
|---|
| 父子 | Props / 回调 | 最常用 |
| 兄弟 | 状态提升 | 简单场景 |
| 跨层级 | Context | 避免过度使用 |
| 全局 | Zustand / Redux | 大型应用 |
| 任意组件 | 状态管理库 | 推荐 Zustand |
核心要点:
- 优先使用 Props 进行父子通信
- 状态提升解决兄弟通信
- Context 解决跨层级传递
- 复杂状态使用状态管理库
- 避免 Props Drilling