React組件間通信


這周重點學習React的組件間通信邮丰。

1. 組件間通信

React的組件間通信可分為四種:

  • 父組件向子組件通信
  • 子組件向父組件通信
  • 跨級組件間通信
  • 無嵌套關系組件間通信

1.1.父組件向子組件通信

因為React的數(shù)據(jù)流是單向流動的走搁,所以父組件向子組件通信也是最常見的通信方式焕檬。父組件通過props向子組件傳遞數(shù)據(jù)。
舉一個簡單的例子:

子組件中:
import React from 'react';

export default function Child({ name }) {
    return <h1>Hello, {name}</h1>;
}

父組件中:
import React, { Component } from 'react';

import Child from './Child';

class Parent extends Component {
    render() {
        return (
            <div>
                <Child name="Sara" />
            </div>
        );
    }
}

export default Parent;

上述例子中父組件向子組件傳遞了name屬性,在子組件中就可以用this.props.name的方式獲取從父組件傳來的name值焊傅。

1.2.子組件向父組件通信

子組件向父組件通信主要有兩種方法:
1.利用回調函數(shù):子組件更新組件狀態(tài)策幼,通過回調函數(shù)的方式傳遞給父組件。
2.利用自定義事件機制:這種方法更加通用廣泛左医,加入事件機制可以簡化組件API授帕。
舉個例子:

父組件中:定義回調函數(shù)
import React, { Component } from 'react';
import Child from './child.js'; 

class App extends Component {
  constructor(props){
    super(props);
    this.state = {
      msg: '父組件初始msg'
    }
  }

  //父組件回調函數(shù)同木,更新state,進而更新父組件跛十。
  callback=(msg)=>{
    this.setState({msg});
  }

  render() {
    return (
      <div className="App">
        <p>子組件傳值實驗: {this.state.msg}</p>
        <Child callback={this.callback} ></Child>
      </div>
    );
  }
}

export default App;

子組件中:調用父組件傳來的回調函數(shù)彤路,并把值通過回調函數(shù)傳遞給父組件。
import React from "react";

class Child extends React.Component{
    constructor(props){
        super(props);
        this.state={
            msg: '子組件msg傳值'
        }
    }
    //通過props調用回調函數(shù)傳值
    trans=()=>{
        this.props.callback(this.state.msg);
    }
    render(){
        return(
            <div>
                <button onClick={this.trans}>激發(fā)trans事件芥映,傳值給父組件</button>
            </div>
        )
    }
}

export default Child;

上述例子中洲尊,在父組件中定義了一個回調函數(shù),可以實時更新傳入的參數(shù)為this.state中的值奈偏,然后在子組件中通過this.props.回調函數(shù)調用它坞嘀,并把自己的this.state中的值傳給這個回調函數(shù),從而達到子組件向父組件傳值的作用惊来。

1.3.跨級組件間通信

在React中丽涩,可以使用context來實現(xiàn)跨級父子組件間的通信。context提供了一種組件之間共享數(shù)據(jù)的方式裁蚁,可以避免數(shù)據(jù)在組件樹上逐層傳遞矢渊,但大部分情況下,不推薦使用context,因為它屬于全局變量枉证,容易引起結構混亂昆淡。如果真的需要使用,建議寫成高階組件來實現(xiàn)刽严。
注:


Context API的使用基于生產者消費者模式昂灵。
生產者一方,通過組件靜態(tài)屬性childContextTypes聲明舞萄,然后通過實例方法getChildContext()創(chuàng)建Context對象眨补。
消費者一方,通過組件靜態(tài)屬性contextTypes申請要用到的Context屬性倒脓,然后通過實例的context訪問Context的屬性撑螺。


舉個例子:

生產者:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import CppComponent from './Cpp.js';

class App extends Component {

  constructor(props){
    super(props);
  }

  //Context生產者,通過靜態(tài)屬性childContextTypes聲明提供給子組件的Context對象的屬性崎弃,
  static childContextTypes = {
    propA: PropTypes.string
  }
  
  //實例getChildContext方法甘晤,返回Context對象
  getChildContext () {
    return {
      propA: 'propA'
    }
  }

  render() {
    return <BppComponent />
  }
}

class BppComponent extends React.Component {
  render () {
    return <CppComponent />
  }
}

export default App;

消費者:
import React, { Component } from 'react';
import PropTypes from 'prop-types'

/**
 * 第三層有A(生產者)層直接傳遞數(shù)據(jù)到此層C(消費者)
 */
class CppComponent extends React.Component {
   //子組件需要通過一個靜態(tài)屬性contextTypes聲明后,才可以訪問父組件Context對象的屬性
  static contextTypes = {
    propA: PropTypes.string
  }

  render () {
    return(
      <div>
        <p>從生產者傳遞過來的屬性A:{this.context.propA}</p>
      </div>
    ) 
  }
}
export default CppComponent;


上面的例子其實就運用了高階組件饲做,高階組件說通俗點就是一個函數(shù)线婚,它接受一個React組件作為參數(shù)輸入,然后輸出一個新的React組件盆均。

1.4.無嵌套關系組件間通信

非嵌套組件: 就是沒有任何包含關系的組件,包括兄弟組件以及不再同一個父級的非兄弟組件塞弊。
使用事件訂閱,即一個發(fā)布者,一個或多個訂閱者游沿。
這里我們借用Node.js Events 模塊的瀏覽器版實現(xiàn)饰抒。

  1. 安裝event
   npm install event -save
  1. 新建Evt.js, 創(chuàng)建EventEmitter 實例
   import { EventEmitter } from 'events'; 
   export default new EventEmitter();
  1. 發(fā)布者通過emit事件觸發(fā)方法,發(fā)布訂閱消息給訂閱者,把 EventEmitter 實例輸出到各組件中使用诀黍;
    訂閱者通過emitter.addListener(事件名稱,函數(shù)名)方法袋坑,進行事件監(jiān)聽(訂閱);通過emitter.removeListener(事件名稱,函數(shù)名)方法 眯勾,進行事件銷毀(取消訂閱)
    舉例:
  發(fā)布者:
  import React, { Component } from 'react';
  import PropTypes from 'prop-types';
  import Custom1 from './Custom1.js';
  import Custom2 from './Custom2.js';
  import emitter from './Evt.js';

  class App extends Component {

    constructor(){
      super();
      this.handleClick = this.handleClick.bind(this);
    }

    handleClick() {
      //emit事件觸發(fā)方法,通過事件名稱找對應的事件處理函callCustom咒彤,將事件處理函數(shù)作為參數(shù)傳入
      emitter.emit('callCustom', 'Hello 我來發(fā)消息了');
    }

    render() {    
      return(
        <div>
          <br/>
          <button onClick = {this.handleClick}>點擊發(fā)布事件</button>
          <Custom1 />
          <Custom2 />
        </div>
      
      )  
    }
  }
  export default App;
  
  
  訂閱者1 Custom1:
  import React from 'react';
  import ReactDOM from 'react-dom';
  import emitter from './Evt.js';

  class Custom1 extends React.Component {

    constructor(){
      super();
      this.state= {
        msg:''
      }
    }

    componentDidMount () { //在組件掛載完成后聲明一個自定義事件
      emitter.addListener('callCustom', (msg) => {
        this.setState({
          msg: 'Custom1收到消息--'+msg
        });
      })
    }

    componentWillUnmount () { //組件銷毀前移除事件監(jiān)聽
      emitter.removeListener('callCustom', (msg) => {
        this.setState({
          msg: 'Custom1即將銷毀此消息--'+ msg
        });
      })
    }

    //訂閱者1消息顯示
    render () {
      return(<p style={{color:'red'}}>
          {this.state.msg}
        </p>) 
    }
  }
  export default Custom1;



訂閱者2 Custom2:
import React from 'react';
import ReactDOM from 'react-dom';
import emitter from './Evt.js';

class Custom2 extends React.Component {

  constructor(){
    super();
    this.state= {
      msg:''
    }
  }

  componentDidMount () { //在組件掛載完成后聲明一個自定義事件
    emitter.addListener('callCustom', (msg) => {
      this.setState({
        msg: 'Custom2收到消息--'+msg
      })
    })
  }

  componentWillUnmount () { //組件銷毀前移除事件監(jiān)聽
    emitter.removeListener('callCustom', (msg) => {
      this.setState({
        msg: 'Custom2即將銷毀此消息--'+ msg
      })
    })
  }

  //訂閱者2消息顯示
  render () {
    return(<p style={{color:'blue'}}>{this.state.msg}</p>) 
  }
}
export default Custom2;

最終預覽效果如下圖:


最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市咒精,隨后出現(xiàn)的幾起案子镶柱,更是在濱河造成了極大的恐慌,老刑警劉巖模叙,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件歇拆,死亡現(xiàn)場離奇詭異,居然都是意外死亡范咨,警方通過查閱死者的電腦和手機故觅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來渠啊,“玉大人输吏,你說我怎么就攤上這事√骝龋” “怎么了贯溅?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長躲查。 經常有香客問我它浅,道長,這世上最難降的妖魔是什么镣煮? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任姐霍,我火速辦了婚禮,結果婚禮上典唇,老公的妹妹穿的比我還像新娘镊折。我一直安慰自己,他們只是感情好介衔,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布恨胚。 她就那樣靜靜地躺著,像睡著了一般夜牡。 火紅的嫁衣襯著肌膚如雪与纽。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天塘装,我揣著相機與錄音急迂,去河邊找鬼。 笑死蹦肴,一個胖子當著我的面吹牛僚碎,可吹牛的內容都是我干的。 我是一名探鬼主播阴幌,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼勺阐,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了矛双?” 一聲冷哼從身側響起渊抽,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎议忽,沒想到半個月后懒闷,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡栈幸,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年愤估,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片速址。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡玩焰,死狀恐怖,靈堂內的尸體忽然破棺而出芍锚,到底是詐尸還是另有隱情昔园,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布并炮,位于F島的核電站蒿赢,受9級特大地震影響,放射性物質發(fā)生泄漏渣触。R本人自食惡果不足惜羡棵,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望嗅钻。 院中可真熱鬧皂冰,春花似錦、人聲如沸养篓。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽柳弄。三九已至舶胀,卻和暖如春概说,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背嚣伐。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工糖赔, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人轩端。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓放典,卻偏偏與公主長得像,于是被迫代替她去往敵國和親基茵。 傳聞我的和親對象是個殘疾皇子奋构,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353