组件通信
组件通信
React 组件间通信常见的几种情况:
- 父组件向子组件通信
- 子组件向父组件通信
- 跨级组件通信
- 非嵌套关系的组件通信
1.父子组件的通信方式?
在 React 中,父子组件的通信是常见的问题,除了使用状态管理工具(如redux
)以外,也可以实现父子组件的相互通信。
其中,父组件可以通过props
、原型方法向子组件通信,子组件可以通过回调函数、事件冒泡向父组件通信。
父组件向子组件通信
props
如下代码,name
作为props
由父组件传递给子组件,子组件拿到name
后,渲染在页面上。
参数name
由父组件传递给了子组件。
import { useState } from "react";
const Son = ({ name }) => {
return <div>{name}</div>;
};
const Father = () => {
const [name, setName] = useState("Jack");
return (
<>
<Son name={name} />
</>
);
};
export default Father;
原型方法
父组件通过React.createRef()
创建Ref
,保存在实例属性myRef
上。父组件中,渲染子组件时,定义一个Ref
属性,值为刚创建的myRef
。
父组件调用子组件的myFunc
函数,传递一个参数,子组件接收到参数,打印出参数。
参数从父组件传递给子组件,完成了父组件向子组件通信。
import React, { Component, Fragment } from "react";
class Son extends Component {
myFunc(name) {
console.log(name);
}
render() {
return <></>;
}
}
// 父组件
export default class Father extends Component {
constructor(props) {
super(props);
// 创建Ref,并保存在实例属性myRef上
this.myRef = React.createRef();
}
componentDidMount() {
// 调用子组件的函数,传递一个参数
this.myRef.current.myFunc("Jack");
}
render() {
return (
<>
<Son ref={this.myRef} />
</>
);
}
}
子组件向父组件通信
回调函数
如下代码所示,父组件显示当前计数值,但不通过父组件本身修改这个值。父组件给子组件传递一个函数,子组件点击按钮时,调用这个函数,实现计数值加一。
在子组件内部,修改了父组件中的值,从而完成了子组件向父组件通信。
import { useState } from "react";
const Son = ({ setCount }) => {
return <button onClick={() => setCount((count) => count + 1)}>点击+1</button>;
};
const Father = () => {
const [count, setCount] = useState(0);
return (
<>
<div>计数值:{count}</div>
<Son setCount={setCount} />
</>
);
};
export default Father;
事件冒泡
如下代码,利用了事件冒泡机制,点击子组件的button
按钮,事件会冒泡到父组件身上,触发父组件的onClick
函数,打印出Jack
。
点击的是子组件,父组件执行函数,完成了子组件向父组件通信。
const Son = () => {
return <button>点击</button>;
};
const Father = () => {
const sayName = (name) => {
console.log(name);
};
return (
<div onClick={() => sayName("Jack")}>
<Son />
</div>
);
};
export default Father;
2.跨级组件的通信方式?
父组件向子组件的子组件通信,向更深层子组件通信:
- 使用 props,利用中间组件层层传递,但是如果父组件结构较深,那么中间每一层组件都要去传递 props,增加了复杂度,并且这些 props 并不是中间组件自己需要的。
- 使用 context,context 相当于一个大容器,可以把要通信的内容放在这个容器中,这样不管嵌套多深,都可以随意取用,对于跨越多层的全局数据可以使用 context 实现。
// context方式实现跨级组件通信
// Context 设计目的是为了共享那些对于一个组件树而言是“全局”的数据
const BatteryContext = createContext();
// 子组件的子组件
class GrandChild extends Component {
render() {
return (
<BatteryContext.Consumer>
{(color) => <h1 style={{ color: color }}>我是红色的:{color}</h1>}
</BatteryContext.Consumer>
);
}
}
// 子组件
const Child = () => {
return <GrandChild />;
};
// 父组件
class Parent extends Component {
state = {
color: "red",
};
render() {
const { color } = this.state;
return (
<BatteryContext.Provider value={color}>
<Child></Child>
</BatteryContext.Provider>
);
}
}
3.非嵌套关系组件的通信方式?
即没有任何包含关系的组件,包括兄弟组件以及不在同一个父级中的非兄弟组件。
- 可以使用自定义事件通信(发布订阅模式)
- 可以通过 redux 等进行全局状态管理
- 如果是兄弟组件通信,可以找到这两个兄弟节点共同的父节点, 结合父子间通信方式进行通信。
4.组件通信的方式有哪些
- ⽗组件向⼦组件通讯: ⽗组件可以向⼦组件通过传 props 的⽅式,向⼦组件进⾏通讯
- ⼦组件向⽗组件通讯: props+回调的⽅式,⽗组件向⼦组件传递 props 进⾏通讯,此 props 为作⽤域为⽗组件⾃身的函 数,⼦组件调⽤该函数,将⼦组件想要传递的信息,作为参数,传递到⽗组件的作⽤域中
- 兄弟组件通信: 找到这两个兄弟节点共同的⽗节点,结合上⾯两种⽅式由⽗节点转发信息进⾏通信
- 跨层级通信: Context 设计⽬的是为了共享那些对于⼀个组件树⽽⾔是“全局”的数据,例如当前认证的⽤户、主题或⾸选语⾔,对于跨越多层的全局数据通过 Context 通信再适合不过
- 发布订阅模式: 发布者发布事件,订阅者监听事件并做出反应,我们可以通过引⼊ event 模块进⾏通信
- 全局状态管理⼯具: 借助 Redux 或者 Mobx 等全局状态管理⼯具进⾏通信,这种⼯具会维护⼀个全局状态中⼼ Store,并根据不同的事件产⽣新的状态
5.如何解决 props 层级过深的问题
- 使用 Context API:提供一种组件之间的状态共享,而不必通过显式组件树逐层传递 props;
- 使用 Redux 等状态库。