跳到主要内容

组件通信

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)

自定义一个发布-订阅模式(适用于非父子组件通信),虽然做到了完全解耦,但是难以追踪数据流,维护困难。

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;
}
},
};