React父子组件通信方式及设计模式在非父子组件通信中的应用

653 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第2天,点击查看活动详情

前言

本文主要总结父子组件在属性与方法之间传递,包括通过props传递属性与方法,以及通过ref获取组件实例方法获取组件属性与方法。其次我们也要关注中间人模式发布-订阅模式生产者与消费者模式在非父子组件通信中的应用。

1.父子组件通信

(1)通过props进行子父通信 (传递数据(父传子)与传递方法(子传父))

传递数据父传子使用props属性传递(参考props属性) 传递方法子传父,将方法当做属性传入子组件,子组件通过props获取该方法,并执行及传递参数 在父组件中: 定义方法,并使用props方式将方法传入子组件

import React, {Component} from 'react';
import Children from "./children";
class Parent extends Component {
    render() {
        return (
            <div>
                <span> 父组件</span>
                <Children events={this.handleEvent}/>
            </div>
        );
    }
    handleEvent(e){
        console.log(e)
    }
}

export default Parent;

在子组件中:

在props中获取父组件传入的方法,注意方法名要与父组件传入相同。

import React, {Component} from 'react';

class Children extends Component {
    render() {
        return (
            <div>
                <button onClick={()=>{
                    this.props.events('子组件触发父组件方法')
                }}>点击</button>
                <span>子组件</span>
            </div>
        );
    }
}

export default Children;

(2)ref标记(父组件拿到子组件的引用,从而调用子组件的方法)

子组件内部维护状态及方法,父组件通过ref获取子组件状态及方法并执行,同vue相似,都是通过ref获取组件实例,从而获取子组件挂载在实例上的方法与属性。 在父组件中: ref绑定子组件,从而获取子组件内的状态与方法。

import React, {Component} from 'react';
import Children from "./children";
class Parent extends Component {
   inputref=React.createRef()
    render() {
        return (
            <div>
                <span> 父组件</span>
                <Children ref={this.inputref} />
                <button onClick={()=>{
                    console.log(this.inputref.current.state.value)
                }}>获取</button>
                <button onClick={()=>{
                    this.inputref.current.clear()
                }}>重置</button>
            </div>
        );
    }
}

export default Parent;

在子组件中:

import React, {Component} from 'react';

class Children extends Component {
    state={
        value:''
    }
    clear(){
        this.setState({
            value:''
        })
    }
    render() {
        return (
            <div>
                <input  value={this.state.value} onChange={(e)=>{
                    this.setState({
                        value:e.target.value
                    })
                }}/>

                <span>子组件</span>
            </div>
        );
    }
}

export default Children;

tip:在父组件中清除子组件的input输入框的value值,this.$refs.form.reset()

2.非父子组件通信

(1)状态提升(中间人模式)
所谓中间人模式其实在Angular中比较常用,当设计组件时,我们的组件应该是内聚的,应该不依赖外部已经存在的组件,要实现这种松耦合的组件Angular中常使用中间人模式,将状态提升。
而在JavaScript中我认为更为准确应当是中介者模式,中介者模式可以解除对象与对象之间的紧耦合关系,当使用中介者模式时,对象与对象之间的通信都是通过中介者来完成,而不是相互引用。 1653573377.jpg 当一个对象状态发生改变时,只需要通知中介者即可,而不需要通知互相引用的对象,这样就从多对多的关系变成了一对多的关系。
React中的状态提升,就是将多个组件需要共享的状态提升到他们最近的父组件上,在父组件上改变这个状态,然后通过props分发给子组件。

(2)发布-订阅模式
发布-订阅模式又叫做观察者模式,是一种一对多的关系,当对象状态发生改变时,将会通知所有的观察者,在JavaScript中,常用事件模型来替代,该模式广泛应用于异步编程,只需要订阅相关状态,我们并不需要知道异步执行过程中的事情,在异步方法执行完毕后获取结果即可。
具体实现: 发布-订阅模式,订阅者将回调函数注册到公众平台,发布者发布时,将回调函数取出执行,并返回发布内容。

1651766365(1).jpg


import React, {Component} from 'react';

class Bus extends Component {
    render() {
        return (
            <div>
                发布订阅模式
            </div>
        );
    }
}
const bus={
    list:[],
    content:'测试数据,订阅者',
    subscribe(callback){
        this.list.push(callback)
    },
    publish() {
        this.list.forEach((callback,index)=>{
            callback(`${this.content}${index+1}号`)
        })
    }
}

bus.subscribe((e)=>{
    console.log(e)
})
bus.subscribe((e)=>{
    console.log(e)
})
bus.publish()
export default Bus;

1651767198(1).jpg

(3)context状态树传参(生产者、消费者模式)

context状态树传参其实是生产者、消费者模式,生产者是指产生数据的模块,消费者是指处理数据的模块,并且生产者与消费者之间需要中介作为缓冲区来缓存数据,这样生产者与消费者之间就不用产生依赖,降低耦合度。

1651767767(1).jpg a.先定义全局context对象

export const GlobalContext=React.createContext()

b.父组件引入GlobalContext,并使用GlobalContext.Provider(生产者)

import React from 'react';
import ReactDOM from 'react-dom/client';
import Bus from './day2/bus'
const root = ReactDOM.createRoot(document.getElementById('root'));
export const GlobalContext=React.createContext()
root.render(
  // <React.StrictMode>
    <GlobalContext.Provider value={
    {
        name:'全局context测试',
    }

}>
    <Bus />
</GlobalContext.Provider>
);

c.子组件引入GlobalContext,并使用GlobalContext.Consumer(消费者)

import React, {Component} from 'react';
import {GlobalContext} from '../index'
class Bus extends Component {
    render() {
        return (
            <GlobalContext.Consumer>
                {
                    (value)=>{
                        return <div>{value.name}</div>
                    }
                }
            </GlobalContext.Consumer>
        )

    }
}

export default Bus;