不借助redux等狀態(tài)管理工具的React組件間的通信解決方法
組件通信分類
React組件間通信分為2大類,3種情況:
- 1)有嵌套關(guān)系的
- 父->子
- 子->父
這里的父通指上級組件,不一定是直屬上級,可跨級
- 2)無嵌套關(guān)系的
- 兄弟組件間
- 無共同上級組件的
父->子(props)
-
方法一:父組件向子組件傳遞 props,子組件得到 props 后進(jìn)行相應(yīng)的處理。(跨多層組件的時候只需要一層一層傳下去就行)
image.png
// 父組件 Parent.js:
import React,{ PureComponent } from "react";
import Children from "./Children.js";
export default class Parent extends PureComponent{
state = {
title: '父組件',
}
outputDesc = () => {
console.log('父組件向子組件通信,通過props傳遞參數(shù)或者函數(shù)');
}
render(){
const { title } = this.state;
const childProps = {
title,
onOutputDesc: this.outputDesc,
}
return(
<Children {...childProps} />
)
}
}
// 子組件 Children.js:
// 界面上會顯示出 【這是子組件獲取到的父組件的title:父組件】
// 點擊控制臺會打印出 【父組件向子組件通信,通過props傳遞參數(shù)或者函數(shù)】
export default class Children extends PureComponent{
render(){
const { title, outputDesc } = this.props;
return(
<Fragment>
<p>這是子組件獲取到的父組件的title:{title}</p>
<button onClick={onOutputDesc}>點擊</button>
</Fragment>
)
}
}
- 方法二:通過context
使用 React提供的context API(Provider 和 Consumer)
對于方法一,如果要通信的組件間的層次結(jié)構(gòu)很深,中間的每一層組件都要去傳遞 props,增加了復(fù)雜性,并且這些 props 并不是這些中間組件自己所需要的。
當(dāng)組件層次在三層以上時建議使用context,context做的事情就是創(chuàng)建一個上下文對象,并且對外暴露提供者(通常在組件樹中上層的位置)和消費者,在上下文之內(nèi)的所有子組件,
都可以訪問這個上下文環(huán)境之內(nèi)的數(shù)據(jù),并且不用通過props??梢岳斫鉃橛幸粋€集中管理state的對象,并限定了這個對象可訪問的范圍,在范圍之內(nèi)的子組件都能獲取到它內(nèi)部的值。
// context.js
// Provider和Consumer總是成對出現(xiàn)
import React from 'react'
const demoContext = React.createContext();
const demoProvider = demoContext.Provider;
const demoConsumer = demoContext.Consumer;
export demoProvider;
export demoConsumer;
// 父組件
import React from 'react'
import List from './List'
import { demoProvider } from './context'
export default class Todo extends React.PureComponent {
state = {
list: [],
}
task = React.createRef()
handleClick = () => {
const list = [...this.state.list, this.task.current.value];
this.setState({ list });
this.task.current.value = '';
}
deleteTask = (index) => {
const { list } = this.state;
list.splice(index, 1);
this.setState({ list });
}
render() {
return (
// 在父(祖先)級組件中把要傳遞內(nèi)容放到value里面
<demoProvider value={{deleteTask: this.deleteTask}}>
<input type="text" ref={this.task}/>
<button onClick={this.handleClick}> 添加 </button>
<List list={this.state.list} deleteTask={this.deleteTask}/>
</demoProvider>
);
}
}
// 子組件
import React from 'react'
import {demoConsumer} from './context'
export default class List extends React.PureComponent{
render() {
const { list } = this.props
return (
<demoConsumer>
// 后代組件中的組件放在Consumer里面, 內(nèi)部是一個函數(shù), 這個函數(shù)接受一個對象作為參數(shù), 參數(shù)是Provider里面提供的值
{
({ deleteTask }) => {
return list.map((item, index) => {
return (
<li key={item}>
{ item }
<button onClick={()=>{deleteTask(index)}}> 刪除 </button>
</li>
)
})
}
}
</demoConsumer>
);
}
}
子->父(回調(diào))
父組件將一個函數(shù)作為 props 傳遞給子組件,子組件調(diào)用該回調(diào)函數(shù),向父組件通信。
// 父組件 Parent.js:
import React,{ PureComponent, Fragment } from "react";
import Children from "./Children.js";
export default class Parent extends PureComponent{
state = {
description: '',
}
handleChangeDesc = (props) => {
this.setState({description: '子組件向父組件通信,通過回調(diào)傳遞參數(shù): 在子組件中點擊按鈕改變父組件state中的屬性})
}
render(){
const { description } = this.state;
const childProps = {
onChangeDesc: this.handleChangeDesc,
}
return(
<Fragment>
<p>{description}</p>
<Children {...childProps} />
</Fragment>
)
}
}
// 子組件 Children.js:
// 界面上會顯示出更新后的description值 【子組件向父組件通信,通過回調(diào)傳遞參數(shù): 在子組件中點擊按鈕改變父組件state中的屬性】
export default class Children extends PureComponent{
changeParentDesc = () => {
const { onChangeDesc } = this.props;
onChangeDesc({description: '子組件向父組件通信,通過回調(diào)傳遞參數(shù): 在子組件中點擊按鈕改變父組件state中的屬性'});
}
render(){
const { title, outputDesc } = this.props;
return(
<button onClick={this.changeParentDesc}>點擊</button>
)
}
}
兄弟組件間
方法一:利用共同的上級組件作為中間人進(jìn)行通信(子1->父->子2)
方法二:通過自定義發(fā)布-訂閱模式的事件實現(xiàn)(以Node.js中events模塊的EventEmitter 類為例,這個會在我的另一篇文章中細(xì)講)
