我是靠谱客的博主 自觉小伙,最近开发中收集的这篇文章主要介绍React 组件通信的五种方式_props_ref_Context_Redux前言1. 父组件向子组件通信2. 子组件向父组件通信3. 跨组件通信 Context4.Redux,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

前言

React 中组件间的通信有以下几种情况:

  • 父组件向子组件通信,可以通过 props 方式传递数据;也可以通过 ref 方式传递数据;
  • 子组件向父组件通信,通过回调函数方式传递数据;
  • 父组件向后代所有组件传递数据,如果组件层级过多,通过 props 的方式传递数据很繁琐,可以通过 Context.Provider 的方式
  • 一个数据源实现跨组件通信,通过指定 contextType 的方式来实现;
  • 多个数据源实现跨组件通信,使用 Context.Consumer 方式实现;

1. 父组件向子组件通信

父组件向子组件通信有两种方式,一是父组件通过属性进行传递,子组件通过 props 接收;二是父组件通过 ref 获取到子组件的实例或者元素,调用子组件的方法进行数据传递。

1.1 通过 props 方式传递数据

父组件中通过给子组件设置属性,将数据传递给子组件,子组件通过 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')
);

1.2 通过 ref 传递数据

React 提供的这个ref属性,表示为对组件真正实例的引用,其实就是 ReactDOM.render() 返回的组件实例,ref 可以挂载到组件上也可以挂载到dom元素上。

  • 挂到组件(class声明的组件)上的 ref 表示对组件实例的引用。不能在函数式组件上使用 ref 属性,因为它们没有实例。
  • 挂载到dom元素上时表示具体的dom元素节点。

使用方法:

  • 父组件使用 ref 属性对子组件做标记,获取到子组件的实例或者dom元素,可以调用子组件的方法,传递数据。
  • 在 React 最新的版本中,要使用 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')
);

2. 子组件向父组件通信

子组件通过 回调函数 向父组件传递数据。父组件将自己的某个方法传递给子组件,子组件通过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')
);

3. 跨组件通信 Context

在 react 中没有类似 vue 中的事件总线来解决这个问题,我们只能借助它们共同的父级组件来实现,将非父子关系装换成多维度的父子关系。

react 中数据是通过 props 属性自上而下(由父及子)进行传递的,但这种做法对于多层级父子关系的组件传值是极其繁琐的。react 提供了context api 来实现在组件之间共享此类值的方式,而不必显式地通过组件树的逐层传递 props。React 16.3之后的contextapi 较之前的好用。

3.1 父组件向后代组件通信

使用 Context.Provider ,具体使用方法如下:

  1. 使用 createContext() 创建一个context 对象(内容为需要传递的数据),参数为默认值;
  2. 在父组件外层使用 <.Provider> 将当前 context 的值传递给内部所有的组件树。当使用了 <.Provider> 后,不再读取上面的默认值,需要设置 value 属性来进行数据传递。
  3. 当组件需要读取数据时,指定 contextType 读取当前的 context 对象(即刚开始创建的 context);
  4. 通过 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> 来实现对多个数据源进行共享。

3.2 多个数据源实现跨组件通信

使用 Context.Consumer ,实现多个数据源跨组件通信。具体使用方法如下:

  1. 使用 import 导入要读取的数据文件
  2. 使用<.Consumer> ,它里面的语法是函数组件的语法,函数接收的 参数 为当前 createContext() 里的默认值。
  3. <.Consumer> 可以嵌套使用,也可以平级使用。

比如现在我们有两个数据文件 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')
);

3.3 Context 的使用场景

Context 主要应用场景在于很多不同层级的组件需要访问同样一些的数据。请谨慎使用,因为这会使得组件的复用性变差。如果用组件组合可以解决的问题,就不要使用 Context

使用 context 的通用的场景包括管理当前的 locale,theme,或者一些缓存数据。

3.4 Context 总结

  • 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 组件在其祖先组件退出更新的情况下也能更新。

4.Redux

上面有提到使用 Context 做组件间的通讯会使得组件的复用性变差,如果项目比较复杂、模块比较很多的情况,推荐使用 Redux 来做组件间的通信。

react-redux 提供两个核心的api:

  • Provider: 提供store,根据单一store原则 ,一般只会出现在整个应用程序的最顶层。
  • connect: 用于连接展示组件和 redux store。

connect 的语法格式为: connect(mapStateToProps?, mapDispatchToProps?, mergeProps?, options?)(component)

一般来说只会用到前面两个,它的作用是:

  • store.getState()的状态转化为展示组件props上的属性
  • actionCreators转化为展示组件props上的方法

特别强调:
官网上的第二个参数为mapDispatchToProps, 实际上就是actionCreators

只要上层中有Provider组件并且提供了store, 那么,子孙级别的任何组件,要想使用store里的状态,都可以通过connect方法进行连接。如果只是想连接actionCreators,可以第一个参数传递为null

点击查看关于Redux的详细介绍

点击查看如何使用 react-redux 实现计数器功能

最后

以上就是自觉小伙为你收集整理的React 组件通信的五种方式_props_ref_Context_Redux前言1. 父组件向子组件通信2. 子组件向父组件通信3. 跨组件通信 Context4.Redux的全部内容,希望文章能够帮你解决React 组件通信的五种方式_props_ref_Context_Redux前言1. 父组件向子组件通信2. 子组件向父组件通信3. 跨组件通信 Context4.Redux所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(35)

评论列表共有 0 条评论

立即
投稿
返回
顶部