本文首發(fā)于公眾號(hào)【一個(gè)老碼農(nóng)】
react組件之間的通信颂暇,大致可以分為以下幾類
- 父?jìng)髯?/li>
- 子傳父
- 兄弟組件之間的通信
- 任意組件之間的通信
- 數(shù)據(jù)全局共享
下面我就來(lái)正式聊一下react
組件之間有哪些通信方式盖奈,并且都是怎樣進(jìn)行通信的活喊,宦言,語(yǔ)言我們用react
+ typescript
1.父?jìng)髯?/h2>
- props
props
傳遞數(shù)據(jù)是我們react
開(kāi)發(fā)過(guò)程中最常用的一種方式敲茄,可以把父組件的數(shù)據(jù)傳遞給子組件焰檩,下面我們就用代碼演示一下:
父組件代碼:
import React from 'react';
import ChildA from './ChildA';
function App() {
return (
<div>
<ChildA title='父?jìng)髯訑?shù)據(jù)'></ChildA>
</div>
)
}
export default App
子組件代碼:
interface ChildAProps {
title: string
}
const ChildA: React.FC<ChildAProps> = ({title}) => {
return <div>{title}</div>
}
export default ChildA
- useContext
useContext
傳遞的數(shù)據(jù)退客,在Context.Provider
的所有子組件中都可以獲取到焚虱,value
一旦更新购裙,所有子組件也會(huì)同步更新
父組件代碼:
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
回調(diào)函數(shù)
向子組件Props
中傳一個(gè)函數(shù),在子組件需要向父組件傳遞數(shù)據(jù)時(shí)鹃栽,調(diào)用此函數(shù)即可
子組件代碼:
interface ChildAProps {
title?: string
call?: (param: string) => void
}
const ChildA: React.FC<ChildAProps> = ({title, call}) => {
return <div onClick={()=>{
call && call('回傳參數(shù)')
}}>這是ChildA {title}</div>
}
export default ChildA
父組件代碼:
import React from 'react';
import ChildA from './ChildA';
function App() {
return (
<div>
<ChildA call={(v)=>{
console.log(`子組件傳過(guò)來(lái)的值 ${v}`)
}}></ChildA>
</div>
)
}
export default App
-
useRef
函數(shù)調(diào)用
ref
的方式可以為父組件提供一個(gè)可以調(diào)用子組件的函數(shù)躏率,在子組件被調(diào)用的函數(shù)中可以返回相應(yīng)的數(shù)據(jù)至父組件,父組件則需要在子組件的Props
中傳入一個(gè)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傳過(guò)來(lái)的數(shù)據(jù)
const title = ref.current.refresh()
console.log(`子組件傳遞過(guò)來(lái)的數(shù)據(jù) ${title}`)
}}>
點(diǎn)擊刷新
</button>
<ChildD cref={ref}></ChildD>
</div>
}
export default ChildC
3.兄弟組件之間的通信
兄弟組件之間的數(shù)據(jù)傳遞民鼓,可以利用組件的Props
以及Props
回調(diào)函數(shù)來(lái)進(jìn)行薇芝,而這種使用方法通信的前提是:必須要有共同的父組件
子組件代碼:
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.任意組件之間的通信
- 觀察者
如果組件之間沒(méi)有共同的父組件,那我們就可以用觀察者進(jìn)行組件之間的通信丰嘉,這個(gè)時(shí)候我們就需要用到第三方的插件events
夯到。下面我們講下如何用events
實(shí)現(xiàn)組件間的通信。
首先我們需要執(zhí)行以下命令安裝event
:
yarn add events
代碼:
安裝成功后饮亏,我們新建一個(gè)event.js
文件耍贾,導(dǎo)出一個(gè)全局對(duì)象
import EventEmitter from "events";
export default new EventEmitter()
接收數(shù)據(jù)的組件代碼:
import { useEffect, useState } from "react"
import event from "./class/event"
const ChildI = () => {
const [message, setMessage] = useState('')
useEffect(() => {
//監(jiān)聽(tīng)消息
event.addListener('message', (message) => {
setMessage(message)
})
return () => {
event.removeListener('message', (message) => {
console.log(message)
})
}
})
return <div>{message}</div>
}
export default ChildI
發(fā)送數(shù)據(jù)的組件代碼:
import event from "./class/event"
const ChildJ = () => {
return <div>
<button onClick={() => {
//發(fā)送消息
event.emit('message','這是我發(fā)的消息')
}}>發(fā)送消息</button>
</div>
}
export default ChildJ
5.各組件數(shù)據(jù)全局共享
-
window
掛載屬性
全局?jǐn)?shù)據(jù)的共享,如果比較簡(jiǎn)單的數(shù)據(jù)路幸,我們可以在window
上掛載屬性荐开。但是在typeScript
中,因?yàn)?code>Window類中沒(méi)有對(duì)應(yīng)的屬性简肴,所以會(huì)提示報(bào)錯(cuò)晃听,我們可以用以下辦法解決:
在src
中創(chuàng)建一個(gè)@types
文件夾,在@types
文件夾下面新建一個(gè)index.d.ts
文件,代碼如下:
interface Window {
customTitle: string
}
有了以上代碼之后就可以在任意地方用以下代碼賦值:
window.customTitle = 'xxx'
取值就用下面代碼:
const customTitle = window.customTitle
console.log(customTitle)
- 自定義單例
上面的window
以及更上面的觀察者其實(shí)都是一個(gè)單例能扒,window
掛載屬性雖然方便佣渴,但是違背了設(shè)計(jì)模式的單一原則,而且并不利于代碼管理和維護(hù)赫粥。在開(kāi)發(fā)中,個(gè)人其實(shí)還是建議自定義一個(gè)單例進(jìn)行管理予借。
我們新建一個(gè)class
越平,并導(dǎo)出一個(gè)全局對(duì)象
export class Single {
title?: string
}
export default new Single()
使用時(shí),我們這樣寫(xiě):
import single from './class/Single';
//賦值
single.title = 'abc'
//取值
const title = single.title
console.log(title)
當(dāng)然灵迫,做為一個(gè)習(xí)慣其它強(qiáng)類型語(yǔ)言的程序員秦叛,我們更熟悉這種寫(xiě)法:
export class Single {
private static instance: Single
public title: string
private constructor(title: string) {
this.title = title
}
/**
* 獲取單例對(duì)象
*/
public static getInstance() {
if (!this.instance) {
this.instance = new Single('')
}
return this.instance
}
}
使用時(shí)這樣使用:
//賦值時(shí)這樣寫(xiě)
Single.getInstance().title = 'xxxx'
//取值使用這樣寫(xiě)
const title = Single.getInstance().title
console.log(title)