react组件之间的通信

本文首发于公众号【一个老码农】

react组件之间的通信,大致可以分为以下几类

  • 父传子
  • 子传父
  • 兄弟组件之间的通信
  • 任意组件之间的通信
  • 数据全局共享

下面我就来正式聊一下react组件之间有哪些通信方式,并且都是怎样进行通信的,,语言我们用react + typescript

1.父传子

  • props

props传递数据是我们react开发过程中最常用的一种方式,可以把父组件的数据传递给子组件,下面我们就用代码演示一下:

父组件代码:

import React from 'react';
import ChildA from './ChildA';

function App() {
  return (
    <div>
      <ChildA title='父传子数据'></ChildA>
    </div>
  )
}

export default App

子组件代码:

interface ChildAProps {
    title: string
}
const ChildA: React.FC<ChildAProps> = ({title}) => {
    return <div>{title}</div>
}

export default ChildA
  • useContext

useContext传递的数据,在Context.Provider的所有子组件中都可以获取到,value一旦更新,所有子组件也会同步更新

父组件代码:

import React from 'react';
import ChildB from './ChildB';

export const Context = React.createContext<String>('')

function App() {
  return (
    <Context.Provider value={'context传值'}>
      <div>
       <ChildB></ChildB>
      </div>
    </Context.Provider>
  )
}

export default App

子组件代码:

import { useContext } from "react"
import { Context } from "./App"

const ChildB = () => {

    const title = useContext(Context)

    return <div>
        这是ChildB {title}
    </div>
}

export default ChildB

2. 子传父

  • Props回调函数

向子组件Props中传一个函数,在子组件需要向父组件传递数据时,调用此函数即可

子组件代码:

interface ChildAProps {
    title?: string
    call?: (param: string) => void
}
const ChildA: React.FC<ChildAProps> = ({title, call}) => {
    return <div onClick={()=>{
        call && call('回传参数')
    }}>这是ChildA {title}</div>
}

export default ChildA

父组件代码:

import React from 'react';
import ChildA from './ChildA';

function App() {
  return (
      <div>
       <ChildA call={(v)=>{
         console.log(`子组件传过来的值 ${v}`)
       }}></ChildA>
      </div>
  )
}

export default App

  • useRef函数调用

ref的方式可以为父组件提供一个可以调用子组件的函数,在子组件被调用的函数中可以返回相应的数据至父组件,父组件则需要在子组件的Props中传入一个ref

子组件代码:

import { MutableRefObject, useImperativeHandle } from "react"

export interface ChildDCurrent {
    refresh: () => string
}

interface ChildDProps {
    cref: MutableRefObject<ChildDCurrent>
}

const ChildD: React.FC<ChildDProps> = ({cref}) => {

    useImperativeHandle(cref, ()=>({
        refresh: () => {
            return '123'
        }
    }))

    return <div></div>
}

export default ChildD

父组件代码:

import { MutableRefObject, useRef } from "react"
import ChildD, { ChildDCurrent } from "./ChildD"

const ChildC = () => {

    const ref = useRef() as MutableRefObject<ChildDCurrent>

    return <div>
        <button onClick={()=> {
            // ChildD传过来的数据
            const title = ref.current.refresh()
            console.log(`子组件传递过来的数据 ${title}`)
        }}>
            点击刷新
        </button>
        <ChildD cref={ref}></ChildD>
    </div>
}

export default ChildC

3.兄弟组件之间的通信

兄弟组件之间的数据传递,可以利用组件的Props以及Props回调函数来进行,而这种使用方法通信的前提是:必须要有共同的父组件

子组件代码:

interface ChildFProps {
    update: (title: string) => void
}
const ChildF: React.FC<ChildFProps> = ({update}) => {
    return <div onClick={() => {
        update('abcde')
    }}>ChildF</div>
}
export default ChildF


interface ChildGProps {
    title: string
}
const ChildG: React.FC<ChildGProps> = ({title}) => {
    return <div>ChildG {title}</div>
}
export default ChildG

父组件代码:

import { useState } from "react"
import ChildF from "./ChildF"
import ChildG from "./ChildG"

const ComE = () => {
    const [updateValue, setUpdateValue] = useState('')
    return <div>
        <ChildF update={(v) => {
            setUpdateValue(v)
        }}></ChildF>
        <ChildG title={updateValue}></ChildG>
    </div>
}
export default ComE

4.任意组件之间的通信

  • 观察者

如果组件之间没有共同的父组件,那我们就可以用观察者进行组件之间的通信,这个时候我们就需要用到第三方的插件events。下面我们讲下如何用events实现组件间的通信。

首先我们需要执行以下命令安装event

yarn add events

代码:
安装成功后,我们新建一个event.js文件,导出一个全局对象

import EventEmitter from "events";

export default new EventEmitter()

接收数据的组件代码:

import { useEffect, useState } from "react"
import event from "./class/event"

const ChildI = () => {
    const [message, setMessage] = useState('')
    useEffect(() => {
        //监听消息
        event.addListener('message', (message) => {
            setMessage(message)
        })
        return () => {
            event.removeListener('message', (message) => {
                console.log(message)
            })
        }
    })

    return <div>{message}</div>
}

export default ChildI

发送数据的组件代码:

import event from "./class/event"

const  ChildJ = () => {
    return <div>
        <button onClick={() => {
            //发送消息
            event.emit('message','这是我发的消息')
        }}>发送消息</button>
    </div>
}

export default ChildJ

5.各组件数据全局共享

  • window挂载属性

全局数据的共享,如果比较简单的数据,我们可以在window上挂载属性。但是在typeScript中,因为Window类中没有对应的属性,所以会提示报错,我们可以用以下办法解决:

src中创建一个@types文件夹,在@types文件夹下面新建一个index.d.ts文件,代码如下:

interface Window {
    customTitle: string
}

有了以上代码之后就可以在任意地方用以下代码赋值:

window.customTitle = 'xxx'

取值就用下面代码:

const customTitle = window.customTitle
console.log(customTitle)
  • 自定义单例

上面的window以及更上面的观察者其实都是一个单例,window挂载属性虽然方便,但是违背了设计模式的单一原则,而且并不利于代码管理和维护。在开发中,个人其实还是建议自定义一个单例进行管理。

我们新建一个class,并导出一个全局对象

export class Single {
    title?: string
}

export default new Single()

使用时,我们这样写:

import single from './class/Single';
//赋值
single.title = 'abc'

//取值
const title = single.title
console.log(title)

当然,做为一个习惯其它强类型语言的程序员,我们更熟悉这种写法:

export class Single {
    private static instance: Single
    
    public title: string

    private constructor(title: string) {
        this.title = title
    }

    /**
     * 获取单例对象
     */
    public static getInstance() {
        if (!this.instance) {
            this.instance = new Single('')
        }
        return this.instance
    }
}

使用时这样使用:

//赋值时这样写
Single.getInstance().title = 'xxxx'

//取值使用这样写
const title =  Single.getInstance().title
console.log(title)

原文地址

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容