组件通信
React 中数据流动是单向的,其他使用场景需要结合其他机制。
- 简单父子通信 Props
- 兄弟组件使用状态提升或 Context
- 复杂应用用 Redux/Zustand
- 避免过度使用事件总线和 Refs 操作子组件
一、父传子
1. 通过 Props
这是最常见的一种数据流动方式,父组件通过 Props 传递数据给子组件。
// 父组件
function Parent() {
const [data] = useState('数据来自父元素');
return <Child message={data} />;
}
// 子元素
function Child({ message = '父组件未传值' }) {
return <div>{message}</div>;
}
二、子传父
1. 回调函数
子组件通过父组件传递的回调函数通知父组件状态变化。
// 父组件
function Parent() {
const handleData = data => console.log(data);
return <Child onSendData={handleData} />;
}
// 子组件
function Child({ onSendData }) {
return <button onClick={() => onSendData('数据来自于子元素')}>发动</button>;
}
2.使用 Refs 和 ForwardRef
父组件直接访问子组件的 DOM 或方法(不推荐常规数据传递)
// 父组件
function Parent() {
const childRef = useRef();
const handleClick = () => childRef.current.doSomething();
return (
<>
<Child ref={childRef}></Child>
<button onclick={handleClick}>触发事件</button>
</>
);
}
// 子元素
const Child = forwardRef((props, ref) => {
useImperativeHandle(ref, () => {
doSOmething: () => console.log('子组件方法被触发');
});
});
三、跨组件通信
1. 内置的 Content
使用 React 内置的方法 React.createContext() 构建跨层级通信。
// 创建 Context
const ThemeContext = React.createContext();
// 父组件
function App() {
return (
<ThemeContext.Provider value={{ color: 'red', fontSize: '16px' }}>
<Header />
<Content />
</ThemeContext.Provider>
);
}
// 子组件
class Header extends React.Component {
static contextType = ThemeContext; // 声明消费 Context
render() {
return <div style={{ color: this.context.color }}>头部</div>;
}
}
// 子组件
function Content() {
// 使用 useContext Hook
const theme = useContext(ThemeContext);
return <div style={{ color: theme.color }}>内容</div>;
}
信息
同样可用于兄弟间传值,此外,兄弟组件间传值还可以使用状态提升。
四、全局状态管理
对于大型应用,可使用 Redux、MobX 等库集中管理状态,解决多组件共享状态的问题。
五、 URL 参数(路由传参)
通过路由(如 React Router )传递参数
// 路由配置
<Route path="/user/:id" component={UserPage} />;
// 组件中获取
function UserPage({ match }) {
const id = match.params.id;
return <div>用户 id:{id}</div>;
}
六、LocalStorage/SessionStorage
在持久化储存数据使用场景,可用于跨组件、跨页面数据共享。但是需要手动同步和清理
七、事件总线(Event Bus)
自定义一个发布-订阅模式(适用于非父子组件通信),虽然做到了完全解耦,但是难以追踪数据流,维护困难。
- 事件总线
- A 组件(发送事件)
- B 组件(监听事件)
eventBus.js
const events = {};
export default {
// 注册事件
on(event, callback) {
events[event] ||= [];
events[event].push(callback);
},
// 弹出事件
emit(event, data) {
if (events[event]) events[event].forEach(cb => cb(data));
},
// 清理事件
off(event, callback) {
if (events[event] === callback) {
events[event] = null;
}
},
};
ComponentA.jsx
import eventBus from './eventBus';
function ComponentA() {
return (
<button onClick={() => eventBus.emit('update', '新数据')}>发送</button>
);
}
ComponentB.jsx
import eventBus from './eventBus.js';
function ComponentB() {
useEffect(() => {
const handleUpdate = data => console.log(data);
eventBus.on('update', handleUpdate);
// 清理
return () => eventBus.off('update', handleUpdate);
}, []);
}