React 中组件间的通信有以下几种情况:
父组件向子组件通信有两种方式,一是父组件通过属性进行传递,子组件通过 props 接收;二是父组件通过 ref 获取到子组件的实例或者元素,调用子组件的方法进行数据传递。
父组件中通过给子组件设置属性,将数据传递给子组件,子组件通过 props 来接收,当父组件更改自己状态的时候,子组件接收到的属性就会发生改变。
import React, {
Component } from 'react'
import ReactDOM from 'react-dom';
//子组件
class Child extends Component{
render(){
return <h1>接收到数据为:{
this.props.num}</h1>
}
}
//父组件
class Parent extends Component{
num=3;
render(){
return <Child num={
this.num}></Child>
}
}
ReactDOM.render(
<Parent/>,
document.getElementById('root')
);
React 提供的这个ref
属性,表示为对组件真正实例的引用,其实就是 ReactDOM.render()
返回的组件实例,ref
可以挂载到组件上也可以挂载到dom元素上。
class
声明的组件)上的 ref 表示对组件实例的引用。不能在函数式组件上使用 ref 属性,因为它们没有实例。使用方法:
ref
属性对子组件做标记,获取到子组件的实例或者dom元素,可以调用子组件的方法,传递数据。ref
,需要通过 React.createRef()
方法生成一个ref
。import React, {
Component ,createRef} from 'react'
import ReactDOM from 'react-dom';
//子组件
class Child extends Component{
state={
name:"admin"
}
childClickHandle=(city)=>{
this.setState({
address:city
})
}
render(){
return (
<div>name:{
this.state.name},address:{
this.state.address}</div>
)
}
}
//父组件
class Parent extends Component{
constructor(){
super();
//通过 createRef() 生成ref
this.childComp=createRef()
}
clickHandle=()=>{
//调用子组件的方法,并传递数据
this.childComp.current.childClickHandle("beijing");
}
render(){
return (
<div>
<button onClick={
this.clickHandle}>按钮</button>
//给子组件设置ref属性
<Child ref={
this.childComp}></Child>
</div>
)
}
}
ReactDOM.render(
<Parent/>,
document.getElementById('root')
);
子组件通过 回调函数
向父组件传递数据。父组件将自己的某个方法传递给子组件,子组件通过this.props
接收到父组件的方法后进行调用。
如果子组件内需要修改父组件传递过来的数据,需要通过调用父组件的方法,在父组件中对数据进行修改。
import React, {
Component ,createRef} from 'react'
import ReactDOM from 'react-dom';
//子组件
class Child extends Component{
state={
name:"admin",
age:18
}
childClickHandle=()=>{
this.props.showInfo({
address:"beijing"})
}
render(){
return (
<div>
//方式一:直接调用父组件的方法
<button onClick={
this.props.showInfo.bind(this,this.state)}>按钮</button>
//方式二:先调用自身的方法,再调用父组件的方法
<button onClick={
this.childClickHandle}>按钮</button>
</div>
)
}
}
//父组件
class Parent extends Component{
clickHandle(data){
//data为子组件中传递过来的数据
//{address: "beijing"}
//{name: "admin", age: 18, sex: "woman"}
console.log(data);
}
render(){
return <Child showInfo={
this.clickHandle.bind(this)}></Child>
}
}
ReactDOM.render(
<Parent/>,
document.getElementById('root')
);
在 react 中没有类似 vue 中的事件总线来解决这个问题,我们只能借助它们共同的父级组件来实现,将非父子关系装换成多维度的父子关系。
react 中数据是通过 props 属性自上而下(由父及子)进行传递的,但这种做法对于多层级父子关系的组件传值是极其繁琐的。react 提供了context
api 来实现在组件之间共享此类值的方式,而不必显式地通过组件树的逐层传递 props。React 16.3之后的context
api 较之前的好用。
使用 Context.Provider ,具体使用方法如下:
createContext()
创建一个context 对象(内容为需要传递的数据),参数为默认值;<.Provider>
将当前 context 的值传递给内部所有的组件树。当使用了 <.Provider>
后,不再读取上面的默认值,需要设置 value 属性来进行数据传递。contextType
读取当前的 context 对象(即刚开始创建的 context);this.context
,获取到当前 context 内的数据;import React, {
Component ,createContext} from 'react';
import ReactDOM from 'react-dom';
//使用createContext()创建一个context,参数为默认值
const cityContext = createContext("beijing");
cityContext.displayName = "cityContextName"; //devtools中调试用
class Parent extends Component{
render() {
return (
//使用Provider 将当前context的值传递给下面的组件树
<cityContext.Provider value="shenzhen">
<Child />
</cityContext.Provider>
)
}
}
//中间的组件不需要对数据进行传递
class Child extends Component{
render() {
return <Grandson />
}
}
class Grandson extends Component{
//指定contextType 读取当前的context
//react 会往上找到最近的 Provider,然后使用它的值
static contextType = cityContext;
render() {
return <div>{
this.context}</div>
//最终页面上输出 shenzhen
}
}
ReactDOM.render(
<Parent/>,
document.getElementById('root')
);
上面的方法中,我们实现了跨组件的数据传递,这种方式的缺点是只能有一个共享的数据源,也就是在 Grandson 组件
中,指定 contextType 的值只能是一个
。那么如果我们有多个数据源都需要进行跨组件传递,应该怎么做呢?这里,我们可以使用 <.Consumer>
来实现对多个数据源进行共享。
使用 Context.Consumer ,实现多个数据源跨组件通信。具体使用方法如下:
导入要读取的数据文件
。函数组件的语法
,函数接收的 参数
为当前 createContext() 里的默认值。比如现在我们有两个数据文件 CityContext.js 和 WeatherContext.js (注意:共享的数据源文件需要导出 context 对象)。
CityContext.js 文件内容:
/* CityContext.js */
import {
createContext} from 'react'
const CityContext = createContext({
id:1,
name:"beijing",
location:"north"
})
export default CityContext;
WeatherContext.js 文件内容:
/* WeatherContext.js */
import {
createContext} from 'react'
const WeatherContext = createContext({
status:"sunshine"
})
export default WeatherContext;
上面这两个文件需要在多个组件内进行使用,可以使用下面的方式:
import React, {
Component } from 'react';
import ReactDOM from 'react-dom';
//导入需要使用的context对象
import CityContext from './CityContext';
import WeatherContext from './WeatherContext';
class Child extends Component{
render() {
return (
<CityContext.Consumer> //子元素是函数组件的语法
{
(prop)=>{
//参数是当前context对象里的数据
return (
<div>
name:{
prop.name},location:{
prop.location}
<WeatherContext.Consumer> //可以嵌套使用,也可以同级使用
{
({
status})=>{
return <div>weather:{
status}</div>
}
}
</WeatherContext.Consumer>
</div>
)
}
}
</CityContext.Consumer>
)
}
}
ReactDOM.render(
<Child />,
document.getElementById('root')
);
Context 主要应用场景在于很多不同层级的组件需要访问同样一些的数据。请谨慎使用,因为这会使得组件的复用性变差。如果用组件组合可以解决的问题,就不要使用 Context 。
使用 context 的通用的场景包括管理当前的 locale,theme,或者一些缓存数据。
React.createContext(defaultValue)
创建一个 Context 对象。Class.contextType
,挂载在 class 上的 contextType 属性会被赋值为一个 Context 对象。这能让你使用 this.context
来消费最近的 Context 上的数据。你可以在任何生命周期中访问到它,包括 render 函数中。Context.Provider
,接收一个 value 属性,传递给消费组件。一个 Provider 可以和多个消费组件有对应关系。多个 Provider 也可以嵌套使用,里层的会覆盖外层的数据。Context.Consumer
,是函数作为子元素(function as a child)这种做法。这个函数参数接收当前的 context 值,返回一个 React 节点。传递给函数的 value
等同于往上组件树离这个 context 最近的 Provider 提供的 value 值
。如果没有对应的 Provider,value 参数等同于传递给 createContext()
的 defaultValue
。Context.displayName
,浏览器中调试用。Consumer 一般情况下都是和 Provider 同时使用。
当 Provider 的 value 值发生变化时,它内部的所有消费组件都会重新渲染。Provider 及其内部 consumer 组件都不受制于 shouldComponentUpdate 函数,因此当 consumer 组件在其祖先组件退出更新的情况下也能更新。
上面有提到使用 Context
做组件间的通讯会使得组件的复用性变差
,如果项目比较复杂、模块比较很多的情况,推荐使用 Redux
来做组件间的通信。
react-redux 提供两个核心的api:
connect 的语法格式为: connect(mapStateToProps?, mapDispatchToProps?, mergeProps?, options?)(component)
一般来说只会用到前面两个,它的作用是:
store.getState()
的状态转化为展示组件props
上的属性actionCreators
转化为展示组件props
上的方法特别强调:
官网上的第二个参数为mapDispatchToProps, 实际上就是actionCreators
只要上层中有Provider
组件并且提供了store
, 那么,子孙级别的任何组件,要想使用store
里的状态,都可以通过connect
方法进行连接。如果只是想连接actionCreators
,可以第一个参数传递为null
。
点击查看关于Redux的详细介绍
点击查看如何使用 react-redux 实现计数器功能