最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 从Vue2.0到React17——React组件之间的通讯

    正文概述 掘金(红尘炼心)   2021-06-25   503

    “这是我参与更文挑战的第5天,活动详情查看: 更文挑战”

    前言

    React组件之间的关系可以分为父子关系,跨级关系,非嵌套关系。本文主要介绍一下各种关系的React组件之间是如何通讯的。

    1、父子关系的React组件的通讯

    父子关系的React组件的通讯,包括父组件给子组件传递数据和子组件改变父组件的数据两种通讯。

    • 将父组件的state赋值给子组件的props实现父组件向子组件的通讯。
    • 将父组件中改变自己数据的函数赋值给子组件的props,在子组件中调用该函数实现子组件向父组件的通讯

    用一个例子来介绍,在HelloWorld组件中把父组件中的infostate展示出来,并且点击HelloWorld组件会改变父组件中的infostate。

    • 类组件的写法:
    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}/>
      )
    }
    

    将父组件的infostate赋值给子组件的titleprop,完成父组件向子组件的通讯,然后子组件中把titleprop展示出来,即把父组件的infostate展示出来。

    在父组件中一个可以改变infostate的handleChangeTitle函数,并把该函数传递给子组件的changeTitleprop,在子组件中执行changeTitle即执行父组件中的handleChangeTitle函数,然后会改变infostate,完成子组件向父组件的通讯。

    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,并把infostate赋值给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.contextvalue可以获取到这些默认数据。

    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中的num1state和组件B中的num2state进行通讯,改变组件A中的num1state,组件B中的num2state也会改变。下面来实现一下:

    组件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的num1state和组件B的num2提取到组件C中,用numstate存储,再把numstate通过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.$onvm.$offvm.$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.$onvm.$offvm.$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”。


    起源地下载网 » 从Vue2.0到React17——React组件之间的通讯

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元