“这是我参与更文挑战的第5天,活动详情查看: 更文挑战”
前言
React组件之间的关系可以分为父子关系,跨级关系,非嵌套关系。本文主要介绍一下各种关系的React组件之间是如何通讯的。
1、父子关系的React组件的通讯
父子关系的React组件的通讯,包括父组件给子组件传递数据和子组件改变父组件的数据两种通讯。
- 将父组件的state赋值给子组件的props实现父组件向子组件的通讯。
- 将父组件中改变自己数据的函数赋值给子组件的props,在子组件中调用该函数实现子组件向父组件的通讯
用一个例子来介绍,在HelloWorld组件中把父组件中的info
state展示出来,并且点击HelloWorld组件会改变父组件中的info
state。
- 类组件的写法:
import React from 'react';
class HelloWorld extends React.Component {
constructor(props) {
super(props);
this.handleChangeTitle=this.handleChangeTitle.bind(this);
}
handleChangeTitle(){
this.props.changeTitle('hello React');
}
render() {
return (
<div>
{this.props.title}
<button
onClick={this.handleChangeTitle.bind(this)}>
改变标题
</button>
</div>
);
}
}
HelloWorld.defaultProps = {
title: 'hello world'
};
export default HelloWorld;
import HelloWorld from './HelloWorld.js'
import React from 'react'
class Index extends React.Component {
constructor(props) {
super(props);
this.state={
info:'hello world'
};
this.handleChangeTitle=this.handleChangeTitle.bind(this);
}
handleChangeTitle(data){
this.setState(state =>{
return {
info:data
}
})
}
render() {
return (
<HelloWorld
title={this.state.info}
changeTitle={this.handleChangeTitle}>
</HelloWorld>
);
}
}
export default Index;
- 函数组件的写法:
export default function HelloWorld(props: any) {
const { title = 'hello world', changeTitle } = props;
const handleChangeTitle = () => {
changeTitle('hello React')
}
return (
<div>
{title}
<button onClick={handleChangeTitle}>改变标题</button>
</div>
);
}
import { useState } from 'react'
import HelloWorld from './HelloWorld.js'
export default function Index(){
const [info,setInfo] = useState('hello world');
const handleChangeTitle = (data)=>{
setInfo(data);
}
return (
<HelloWorld title={info} changeTitle={handleChangeTitle}/>
)
}
将父组件的info
state赋值给子组件的title
prop,完成父组件向子组件的通讯,然后子组件中把title
prop展示出来,即把父组件的info
state展示出来。
在父组件中一个可以改变info
state的handleChangeTitle
函数,并把该函数传递给子组件的changeTitle
prop,在子组件中执行changeTitle
即执行父组件中的handleChangeTitle
函数,然后会改变info
state,完成子组件向父组件的通讯。
2、跨级关系的React组件的通讯
跨级关系的组件,比如祖先组件和后代组件之间的关系就是跨级关系,跨级关系的React组件不能像父子关系的React组件之间那样来通讯。
比如祖父组件要给孙子组件传递一个数据,总不能在组件之间一层一层的传递props
,多么繁琐。
在Vue使用provide
在祖先组件中注入一些数据,在后代组件中用inject
来接受这些数据,称做依赖注入。
而在React也有类似的功能,叫做Context。
先用React.createContext
创建一个Context,然后用Context.Provider
给祖先组件注入一些数据。然后在后代组件中用Class.contextType
或者Context.Consumer
来获取这些数据。
- 类组件的写法
先在context.js文件中创建一个Context。
import React from 'react';
export const MyContext = React.createContext(
//在这里可以设置一些默认值
{title:'hello world'}
);
在祖先组件Grandfather中从context.js引入MyContext,然后用MyContext.Provider
包裹子组件Father,并把info
state赋值给MyContext.Provider
上的属性value
,相当于注入数据。
import React from 'react';
import Father from './Father.js';
import { MyContext } from './context.js';
export default class Grandfather extends React.Component {
constructor(){
super();
this.state={
info:{
title:'hello react'
}
}
}
render() {
return (
<MyContext.Provider value={this.state.info}>
<Father>
</Father>
</MyContext.Provider>
)
}
}
在Father组件中引入子组件Son。
import React from 'react';
import Son from './Son.js';
export default class Father extends React.Component{
render(){
return(
<Son></Son>
)
}
}
此时,组件Son就是组件Grandfather的后代组件。可以看到组件Father并没有通过props
从祖先组件Grandfather中获取数据info,再通过props
传递给组件Son,那么在组件Son中怎么才能获取到数据info呢?
在类组件中,先把MyContext赋值到类组件中的静态属性contextType
,然后用this.context
来获取该组件的上级组件中最近一个被MyContext.Provider
包裹的组件中传递给MyContext.Provider
的属性value
的数据。
import React from 'react';
import { MyContext } from './context.js';
export default class Son extends React.Component {
static contextType = MyContext;
render() {
return (
<div>
{this.context.title}
</div>
)
}
}
在函数组件中,用MyContext.Consumer
包裹一个函数式组件,该函数式组件的参数,可以接收该组件的上级组件中最近一个被MyContext.Provider
包裹的组件中传递给MyContext.Provider
的属性value
的数据。
import React from 'react';
import { MyContext } from './context.js';
export default class Son extends React.Component {
static contextType = MyContext;
render() {
return (
<MyContext.Consumer>
{value => (<div>{value.title}</div>)}
</MyContext.Consumer>
)
}
}
还记得用React.createContext
创建Context,可以接受一些数据,作为默认数据。如果使用Class.contextType
或者Context.Consumer
的组件的上级组件中没有一个被MyContext.Provider
包裹,那么this.context
和value
可以获取到这些默认数据。
3、非嵌套关系的React组件的通讯
非嵌套关系的组件,比如兄弟组件之间的关系就是非嵌套关系。可以给兄弟组件创建一个共同父组件,用父组件作为一个中转站,来处理兄弟组件的之间的通讯。
例如,组件A和组件B是两个兄弟组件,创建一个共同父组件C,把组件A和组件B要通讯的state提取到父组件C的state中,再赋值给组件A和组件B的props,传递到组件A和组件B中。然后在父组件C中定义一个修改state的函数,再把函数赋值给组件A和组件B的props。那么在组件A或组件B调用这个函数的prop,改变父组件C的state,在通过props把改变后的state传递给组件A或组件B,即完成了兄弟组件的通讯。
例如:要组件A中的num1
state和组件B中的num2
state进行通讯,改变组件A中的num1
state,组件B中的num2
state也会改变。下面来实现一下:
组件A:
import React,{useState} from 'react';
const A = () =>{
const [num1,setNum1] = useState(1)
return (
<div>{num1}</div>
)
}
export default A;
组件B:
import React,{useState} from 'react';
const B = () =>{
const [num2,setNum2] = useState(2)
return (
<div>{num2}</div>
)
}
export default B;
先创建一个父组件C把组件A和组件B包裹进去,把组件A的num1
state和组件B的num2
提取到组件C中,用num
state存储,再把num
state通过props传递给组件A和组件B。同时定义一个能改变num
的函数,通过props传递传递给组件A和组件B,再组件A和组件B执行该函数,即可以实现组件A和组件B的通讯。
组件C
import React,{useState} from 'react';
import A from './A';
import B from './B';
const C = () =>{
const [num,setNum] = useState(1)
const handleChangNum = (data)=>{
setNum(data)
}
return (
<div>
<A num1={num} changNum={handleChangNum}></A>
<B num2={num} changNum={handleChangNum}></B>
</div>
)
}
export default C;
组件A
import React from 'react';
const A = (props) =>{
const {num1,changNum} = props;
const handleChang = () =>{
//改变组件B中的num2
changNum(2);
}
return (
<div onClick={handleChang}>{num1}</div>
)
}
export default A;
组件B
import React from 'react';
const B = (props) =>{
const {num2,changNum} = props;
const handleChang = () =>{
//改变组件A中的num1
changNum(2);
}
return (
<div onClick={handleChang}>{num1}</div>
)
}
export default B;
4、使用Events Bus来进行通讯
回顾Vue中用vm.$on
、vm.$off
、vm.$emit
进行组件通讯,其原理就是Events Bus。在React中没有提供类似的API,我们可以利用node.js提供的events.js来实现,其文档可以点这里。
首先执行npm install events --save
下载events.js。再创建一个eventbus.js
,在里面引入events.js,并创建一个EventEmitter实例。
import { EventEmitter } from "events";
export default new EventEmitter();
EventEmitter实例提供了类似vm.$on
、vm.$off
、vm.$emit
的实例方法,
emitter.on(eventName, listener)
对应vm.$on( event, callback )
;emitter.off(eventName, listener)
对应vm.$off( [event, callback] )
;emitter.emit(eventName[, ...args])
对应vm.$emit( eventName, […args] )
;
其中eventName
是要监听事件的名称,listener
被监听事件的回调函数,...args
传递给监听事件的回调的参数。
那么,例如两个非嵌套关系的组件A和组件B,也可以通过events.js来实现通讯,可以在组件A的挂载阶段用emitter.on
绑定要监听事件,在组件的卸载阶段用emitter.off
解绑要监听事件,在组件B用emitter.emit
触发被监听事件,把emitter.emit
第二参数开始的参数,当作参数传递给emitter.on
绑定的监听事件的回调函数,即实现组件A中接收到组件B向组件A发起通讯传递数据的功能。
函数组件的实现:
组件A
import React,{useEffect} from 'react';
import emitter from "./ev"
const A = () =>{
const handleChange = (param1,param2)=>{
console.log(param1);
console.log(param2);
}
useEffect(() =>{
emitter.on('chang',handleChange);
return () =>{
emitter.off('chang',handleChange);
}
},[])
return(
<div>A组件</div>
)
}
export default A;
组件B
import React from 'react';
import emitter from "./ev"
const B = () =>{
const handleChange = () =>{
emitter.emit('chang','参数1','参数2')
}
return(
<div onClick={handleChange}>B组件</div>
)
}
export default B;
此时点击组件B,控制台会打印出“参数1”和“参数2”。
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!