| 通信方式 | 适用场景 | 复杂度 | 优点 | 缺点 |
|---|---|---|---|---|
| Props(父传子) | 父子组件简单数据传递 | ★☆☆☆☆ | 简单直观,React核心特性 | 只能单向传递 |
| 回调函数(子传父) | 子组件通知父组件 | ★★☆☆☆ | 实现简单,符合单向数据流 | 回调地狱(多层嵌套) |
| 状态提升 | 兄弟组件间通信 | ★★★☆☆ | 避免引入额外状态管理 | 可能导致父组件臃肿 |
| Context API | 跨层级组件通信 | ★★★★☆ | 避免props drilling | 可能影响性能,适合低频更新 |
| 事件总线/发布订阅 | 任意组件间通信 | ★★★★☆ | 完全解耦,灵活 | 类型不安全,难以调试 |
| 状态管理库(Redux) | 大型应用全局状态 | ★★★★★ | 可预测的状态管理 | 学习成本高,模板代码多 |
// 父组件
function ParentComponent() {
const user = {
name: '张三',
age: 25,
email: 'zhangsan@example.com'
};
const items = ['项目1', '项目2', '项目3'];
return (
<div>
<h2>父组件</h2>
{/* 传递单个数据 */}
<ChildComponent1 name="李四" />
{/* 传递对象 */}
<ChildComponent2 user={user} />
{/* 传递数组 */}
<ChildComponent3 items={items} />
{/* 传递函数 */}
<ChildComponent4 onAction={() => console.log('父组件收到事件')} />
</div>
);
}
// 子组件1 - 接收基本类型props
function ChildComponent1(props) {
return <div>用户名: {props.name}</div>;
}
// 子组件2 - 接收对象props
function ChildComponent2({ user }) { // 使用解构语法
return (
<div>
<p>姓名: {user.name}</p>
<p>年龄: {user.age}</p>
<p>邮箱: {user.email}</p>
</div>
);
}
// 子组件3 - 接收数组props
function ChildComponent3(props) {
return (
<ul>
{props.items.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
);
}
// 子组件4 - 接收函数props
function ChildComponent4({ onAction }) {
return (
<button onClick={onAction}>
触发父组件函数
</button>
);
}
import PropTypes from 'prop-types';
function UserProfile({ user, isOnline, onFollow, children }) {
return (
<div className={`user-profile ${isOnline ? 'online' : 'offline'}`}>
<h3>{user.name}</h3>
<p>年龄: {user.age}</p>
<p>状态: {isOnline ? '在线' : '离线'}</p>
<button onClick={onFollow}>关注</button>
{children}
</div>
);
}
// 定义props类型检查
UserProfile.propTypes = {
user: PropTypes.shape({
name: PropTypes.string.isRequired,
age: PropTypes.number
}).isRequired,
isOnline: PropTypes.bool,
onFollow: PropTypes.func,
children: PropTypes.node
};
// 设置默认值
UserProfile.defaultProps = {
isOnline: false,
onFollow: () => console.log('关注函数未提供')
};
// 使用组件
function App() {
const user = { name: '王五', age: 30 };
return (
<UserProfile user={user} isOnline={true}>
<p>这是children内容</p>
</UserProfile>
);
}
// 父组件
function CounterParent() {
const [count, setCount] = useState(0);
// 回调函数,接收子组件传递的数据
const handleIncrement = (incrementValue) => {
setCount(prevCount => prevCount + incrementValue);
};
const handleReset = () => {
setCount(0);
};
return (
<div>
<h2>父组件计数器: {count}</h2>
{/* 将回调函数传递给子组件 */}
<CounterChild
onIncrement={handleIncrement}
onReset={handleReset}
/>
</div>
);
}
// 子组件
function CounterChild({ onIncrement, onReset }) {
const [step, setStep] = useState(1);
return (
<div>
<h3>子组件控制器</h3>
<div>
<label>步长: </label>
<input
type="number"
value={step}
onChange={(e) => setStep(parseInt(e.target.value) || 1)}
/>
</div>
<div>
{/* 调用父组件传递的回调函数 */}
<button onClick={() => onIncrement(step)}>
增加 {step}
</button>
<button onClick={() => onIncrement(-step)}>
减少 {step}
</button>
<button onClick={onReset}>重置</button>
</div>
</div>
);
}
计数: 0
当多个组件需要共享同一状态时,将状态提升到它们最近的共同父组件中。
// 父组件 - 持有共享状态
function TemperatureCalculator() {
const [temperature, setTemperature] = useState('');
const [scale, setScale] = useState('c');
// 将状态和更新函数传递给子组件
const handleCelsiusChange = (temp) => {
setTemperature(temp);
setScale('c');
};
const handleFahrenheitChange = (temp) => {
setTemperature(temp);
setScale('f');
};
// 温度转换函数
const toCelsius = (fahrenheit) => {
return (fahrenheit - 32) * 5 / 9;
};
const toFahrenheit = (celsius) => {
return (celsius * 9 / 5) + 32;
};
// 根据当前单位和温度计算另一单位的温度
const celsius = scale === 'f' ? toCelsius(parseFloat(temperature) || 0) : parseFloat(temperature) || 0;
const fahrenheit = scale === 'c' ? toFahrenheit(parseFloat(temperature) || 0) : parseFloat(temperature) || 0;
return (
<div>
<h2>温度转换器</h2>
<TemperatureInput
scale="c"
temperature={celsius.toString()}
onTemperatureChange={handleCelsiusChange}
/>
<TemperatureInput
scale="f"
temperature={fahrenheit.toString()}
onTemperatureChange={handleFahrenheitChange}
/>
<BoilingVerdict celsius={celsius} />
</div>
);
}
// 子组件1 - 摄氏温度输入
function TemperatureInput({ scale, temperature, onTemperatureChange }) {
const scaleNames = {
c: '摄氏温度',
f: '华氏温度'
};
return (
<fieldset>
<legend>请输入{scaleNames[scale]}:</legend>
<input
value={temperature}
onChange={(e) => onTemperatureChange(e.target.value)}
/>
</fieldset>
);
}
// 子组件2 - 显示结果
function BoilingVerdict({ celsius }) {
if (celsius >= 100) {
return <p>水会沸腾</p>;
}
return <p>水不会沸腾</p>;
}
// 创建Context
import React, { createContext, useState, useContext } from 'react';
// 1. 创建Context
const UserContext = createContext();
const ThemeContext = createContext();
// 2. 创建Provider组件
function AppProviders({ children }) {
const [user, setUser] = useState(null);
const [theme, setTheme] = useState('light');
const login = (userData) => {
setUser(userData);
};
const logout = () => {
setUser(null);
};
const toggleTheme = () => {
setTheme(prev => prev === 'light' ? 'dark' : 'light');
};
return (
<UserContext.Provider value={{ user, login, logout }}>
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
</UserContext.Provider>
);
}
// 3. 创建自定义Hook简化使用
function useUser() {
const context = useContext(UserContext);
if (!context) {
throw new Error('useUser必须在UserProvider内使用');
}
return context;
}
function useTheme() {
const context = useContext(ThemeContext);
if (!context) {
throw new Error('useTheme必须在ThemeProvider内使用');
}
return context;
}
// 4. 在任意层级的组件中使用
// 深层子组件 - 不需要通过props层层传递
function DeepChildComponent() {
const { user } = useUser();
const { theme, toggleTheme } = useTheme();
return (
<div className={`deep-child ${theme}`}>
{user ? (
<div>
<p>欢迎, {user.name}!</p>
<p>邮箱: {user.email}</p>
</div>
) : (
<p>请先登录</p>
)}
<button onClick={toggleTheme}>
切换主题 (当前: {theme})
</button>
</div>
);
}
// 5. 在应用中使用
function App() {
return (
<AppProviders>
<Header />
<MainContent />
<DeepChildComponent />
<Footer />
</AppProviders>
);
}
// 事件总线实现
class EventBus {
constructor() {
this.events = new Map();
}
// 订阅事件
on(eventName, callback) {
if (!this.events.has(eventName)) {
this.events.set(eventName, []);
}
this.events.get(eventName).push(callback);
// 返回取消订阅函数
return () => {
this.off(eventName, callback);
};
}
// 取消订阅
off(eventName, callback) {
if (!this.events.has(eventName)) return;
const callbacks = this.events.get(eventName);
const index = callbacks.indexOf(callback);
if (index > -1) {
callbacks.splice(index, 1);
}
}
// 发布事件
emit(eventName, data) {
if (!this.events.has(eventName)) return;
const callbacks = this.events.get(eventName);
// 使用slice创建副本,防止在回调中取消订阅导致遍历问题
callbacks.slice().forEach(callback => {
try {
callback(data);
} catch (error) {
console.error(`事件 ${eventName} 处理错误:`, error);
}
});
}
// 一次性订阅
once(eventName, callback) {
const onceCallback = (data) => {
callback(data);
this.off(eventName, onceCallback);
};
this.on(eventName, onceCallback);
}
}
// 创建全局事件总线实例
const eventBus = new EventBus();
// 组件A - 发布事件
function ComponentA() {
const [message, setMessage] = useState('');
const sendMessage = () => {
eventBus.emit('message', {
from: 'ComponentA',
text: message,
timestamp: new Date().toISOString()
});
setMessage('');
};
return (
<div>
<h3>组件A (发布者)</h3>
<input
value={message}
onChange={(e) => setMessage(e.target.value)}
placeholder="输入消息"
/>
<button onClick={sendMessage}>发送消息</button>
</div>
);
}
// 组件B - 订阅事件
function ComponentB() {
const [receivedMessages, setReceivedMessages] = useState([]);
useEffect(() => {
// 订阅消息事件
const unsubscribe = eventBus.on('message', (data) => {
console.log('ComponentB收到消息:', data);
setReceivedMessages(prev => [...prev, data]);
});
// 组件卸载时取消订阅
return unsubscribe;
}, []);
return (
<div>
<h3>组件B (订阅者)</h3>
<div>收到消息数: {receivedMessages.length}</div>
<ul>
{receivedMessages.map((msg, index) => (
<li key={index}>
[{new Date(msg.timestamp).toLocaleTimeString()}]
{msg.from}: {msg.text}
</li>
))}
</ul>
</div>
);
}
// 组件C - 也订阅事件
function ComponentC() {
const [lastMessage, setLastMessage] = useState(null);
useEffect(() => {
const unsubscribe = eventBus.on('message', (data) => {
console.log('ComponentC收到消息:', data);
setLastMessage(data);
});
return unsubscribe;
}, []);
return (
<div>
<h3>组件C (订阅者)</h3>
{lastMessage && (
<div>
<p>最新消息:</p>
<p>来源: {lastMessage.from}</p>
<p>内容: {lastMessage.text}</p>
</div>
)}
</div>
);
}
// 在应用中使用
function EventBusApp() {
return (
<div>
<ComponentA />
<ComponentB />
<ComponentC />
</div>
);
}
// 创建共享状态的自定义Hook
function useSharedState(initialValue) {
const [state, setState] = useState(initialValue);
// 创建ref来存储回调函数
const callbacksRef = useRef(new Set());
// 更新状态并通知所有订阅者
const updateState = useCallback((newValue) => {
setState(newValue);
// 调用所有订阅的回调函数
callbacksRef.current.forEach(callback => {
callback(newValue);
});
}, []);
// 订阅状态变化
const subscribe = useCallback((callback) => {
callbacksRef.current.add(callback);
// 返回取消订阅函数
return () => {
callbacksRef.current.delete(callback);
};
}, []);
return { state, updateState, subscribe };
}
// 使用自定义Hook的组件A
function ComponentWithHookA() {
const { state: sharedValue, updateState } = useSharedState('初始值');
const [localValue, setLocalValue] = useState('');
const handleUpdate = () => {
updateState(localValue);
};
return (
<div>
<h3>组件A (更新者)</h3>
<p>当前共享值: {sharedValue}</p>
<input
value={localValue}
onChange={(e) => setLocalValue(e.target.value)}
placeholder="输入新值"
/>
<button onClick={handleUpdate}>更新共享值</button>
</div>
);
}
// 使用自定义Hook的组件B
function ComponentWithHookB() {
const { state: sharedValue, subscribe } = useSharedState('初始值');
const [currentValue, setCurrentValue] = useState(sharedValue);
useEffect(() => {
// 订阅共享状态变化
const unsubscribe = subscribe((newValue) => {
setCurrentValue(newValue);
});
return unsubscribe;
}, [subscribe]);
return (
<div>
<h3>组件B (订阅者)</h3>
<p>监听到的共享值: {currentValue}</p>
</div>
);
}
// 使用forwardRef和useImperativeHandle暴露组件方法
import React, { forwardRef, useImperativeHandle, useRef } from 'react';
// 子组件 - 暴露方法给父组件
const ChildWithMethods = forwardRef((props, ref) => {
const [count, setCount] = useState(0);
const inputRef = useRef();
// 暴露方法给父组件
useImperativeHandle(ref, () => ({
// 重置计数器
resetCounter: () => {
setCount(0);
},
// 增加计数器
increment: (value = 1) => {
setCount(prev => prev + value);
},
// 获取当前值
getCount: () => {
return count;
},
// 聚焦输入框
focusInput: () => {
inputRef.current?.focus();
},
// 设置值
setValue: (value) => {
setCount(value);
}
}));
return (
<div>
<h3>子组件</h3>
<p>内部计数: {count}</p>
<input
ref={inputRef}
type="text"
placeholder="可被父组件聚焦的输入框"
/>
</div>
);
});
// 父组件 - 调用子组件方法
function ParentCallingChild() {
const childRef = useRef();
const handleReset = () => {
childRef.current?.resetCounter();
};
const handleIncrement = () => {
childRef.current?.increment(5);
};
const handleGetCount = () => {
const count = childRef.current?.getCount();
alert(`当前计数: ${count}`);
};
const handleFocusInput = () => {
childRef.current?.focusInput();
};
const handleSetValue = () => {
childRef.current?.setValue(100);
};
return (
<div>
<h2>父组件</h2>
<div>
<button onClick={handleReset}>重置子组件计数</button>
<button onClick={handleIncrement}>子组件计数+5</button>
<button onClick={handleGetCount}>获取子组件计数</button>
<button onClick={handleFocusInput}>聚焦子组件输入框</button>
<button onClick={handleSetValue}>设置子组件计数为100</button>
</div>
<ChildWithMethods ref={childRef} />
</div>
);
}
// Redux配置 (简化示例)
import { createStore } from 'redux';
import { Provider, useSelector, useDispatch } from 'react-redux';
// 1. 定义action类型
const INCREMENT = 'INCREMENT';
const DECREMENT = 'DECREMENT';
const SET_VALUE = 'SET_VALUE';
// 2. 创建reducer
function counterReducer(state = { count: 0 }, action) {
switch (action.type) {
case INCREMENT:
return { count: state.count + action.payload };
case DECREMENT:
return { count: state.count - action.payload };
case SET_VALUE:
return { count: action.payload };
default:
return state;
}
}
// 3. 创建store
const store = createStore(counterReducer);
// 4. 创建action创建函数
const increment = (value = 1) => ({
type: INCREMENT,
payload: value
});
const decrement = (value = 1) => ({
type: DECREMENT,
payload: value
});
const setValue = (value) => ({
type: SET_VALUE,
payload: value
});
// 组件A - 读取状态
function ComponentA() {
const count = useSelector(state => state.count);
return (
<div>
<h3>组件A</h3>
<p>当前计数: {count}</p>
</div>
);
}
// 组件B - 派发action
function ComponentB() {
const dispatch = useDispatch();
const [inputValue, setInputValue] = useState('');
return (
<div>
<h3>组件B</h3>
<div>
<button onClick={() => dispatch(increment())}>+1</button>
<button onClick={() => dispatch(decrement())}>-1</button>
<button onClick={() => dispatch(increment(5))}>+5</button>
</div>
<div>
<input
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
placeholder="设置计数"
/>
<button onClick={() => {
const value = parseInt(inputValue);
if (!isNaN(value)) {
dispatch(setValue(value));
setInputValue('');
}
}}>
设置值
</button>
</div>
</div>
);
}
// 组件C - 同时读取和派发
function ComponentC() {
const count = useSelector(state => state.count);
const dispatch = useDispatch();
return (
<div>
<h3>组件C</h3>
<p>计数: {count}</p>
<button onClick={() => dispatch(setValue(0))}>
重置
</button>
</div>
);
}
// 应用入口
function ReduxApp() {
return (
<Provider store={store}>
<ComponentA />
<ComponentB />
<ComponentC />
</Provider>
);
}
适用场景:
推荐方案:
Props 回调函数适用场景:
推荐方案:
状态提升 Context API 自定义Hook适用场景:
推荐方案:
Redux MobX 事件总线// 不好的做法:props drilling
function App() {
const [user, setUser] = useState({ name: '张三' });
return (
<div>
<Header user={user} />
<MainContent user={user} />
</div>
);
}
function MainContent({ user }) {
return (
<div>
<Sidebar user={user} />
<Content user={user} />
</div>
);
}
function Content({ user }) {
return (
<div>
<Article user={user} /> {/* 实际可能不需要user */}
</div>
);
}
// 好的做法:使用Context
const UserContext = createContext();
function App() {
const [user, setUser] = useState({ name: '张三' });
return (
<UserContext.Provider value={user}>
<Header />
<MainContent />
</UserContext.Provider>
);
}
// 深层组件直接使用Context,不需要中间组件传递
function Article() {
const user = useContext(UserContext);
// 直接使用user
}
// 使用React.memo避免不必要的重新渲染
const MemoizedChild = React.memo(function Child({ data, onClick }) {
console.log('Child组件渲染');
return (
<div>
<p>数据: {data}</p>
<button onClick={onClick}>点击</button>
</div>
);
});
// 使用useCallback缓存回调函数
function Parent() {
const [count, setCount] = useState(0);
// 使用useCallback避免每次渲染都创建新函数
const handleClick = useCallback(() => {
console.log('按钮被点击');
}, []); // 空依赖数组,函数只创建一次
return (
<div>
<button onClick={() => setCount(count + 1)}>
更新计数: {count}
</button>
<MemoizedChild data="固定数据" onClick={handleClick} />
</div>
);
}
// 创建错误边界组件
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false, error: null };
}
static getDerivedStateFromError(error) {
return { hasError: true, error };
}
componentDidCatch(error, errorInfo) {
// 可以在这里记录错误日志
console.error('组件通信错误:', error, errorInfo);
}
render() {
if (this.state.hasError) {
return (
<div className="error-boundary">
<h3>组件通信出错</h3>
<p>错误信息: {this.state.error?.message}</p>
<button onClick={() => this.setState({ hasError: false })}>
重试
</button>
</div>
);
}
return this.props.children;
}
}
// 使用错误边界包裹组件
function App() {
return (
<ErrorBoundary>
<ComponentA />
<ComponentB />
</ErrorBoundary>
);
}