React 組件間通信

React作為前端的新一代主流框架壳嚎,因其組件化的思想席爽,徹底革新了前端停留在DOM操作的古老開發(fā)方式。使用React的組件蹦玫,不再需要模板赎婚,也不用再過分擔(dān)心渲染和更新帶來的性能問題。開發(fā)一個(gè)個(gè)組件钳垮,就像一個(gè)個(gè)模塊一般惑淳,在需要的地方放在那里就好。但是這么多的組件之間饺窿,如何進(jìn)行信息交流和信息傳遞歧焦,就引發(fā)了一個(gè)新的問題——組件通信。

我是目錄

  1. 事件通信
  2. 觀察者模式
  3. 關(guān)于Redux與Flux

首先肚医,我們先假設(shè)我們的組件結(jié)構(gòu)如下:

      Parent
     ____|____
     |       |
  ChildA  ChildB

1. 事件通信

1.1 父?jìng)髯?/h3>

React 中绢馍,父組件可以向子組件通過傳 props 的方式,向子組件進(jìn)行通訊肠套。

class Parent extends Component {
  state = { msg: 'start' };

  componentDidMount() {
    setTimeout(() => { this.setState({ msg: 'end' }); }, 1000);
  }

  render() {
    // 將傳遞值當(dāng)做組件的props屬性傳遞給子組件
    // return <ChildA msg={this.state.msg} />;
    // 使用...運(yùn)算符將父組件信息以更簡(jiǎn)潔的方式傳遞給子組件
    return <ChildA {...this.state} />;
  }
}

class ChildA extends Component {
  render() {
    // 獲取父組件傳遞過來的數(shù)據(jù)中的msg
    return <p>{this.props.msg}</p>;
  }
}

export default Parent;

1.2 子傳父

子組件向父組件通訊舰涌,同樣也需要父組件向子組件傳遞 props 進(jìn)行通訊,只是父組件傳遞的你稚,是作用域?yàn)楦附M件自身的函數(shù)瓷耙,子組件調(diào)用該函數(shù),將子組件想要傳遞的信息刁赖,作為參數(shù)搁痛,傳遞到父組件的作用域中。

class Parent extends Component {
  state = { msg: 'start' };

  transferMsg(childMsg) {
    this.setState({ msg: childMsg });
  }

  render() {
    return (
      <div>
        <p>{this.state.msg}</p>
        // 將事件傳遞給子組件宇弛,子組件通過事件傳遞參數(shù)與父組件通信
        <ChildA transferMsg={childMsg => this.transferMsg(childMsg)} />
      </div>
    );
  }
};

class ChildA extends Component {
  componentDidMount() {
    setTimeout(() => { this.props.transferMsg('end'); }, 1000);
  }

  render() {
    return <div />;
  }
}

1.3 兄弟組件通信

對(duì)于沒有直接關(guān)聯(lián)關(guān)系的兩個(gè)節(jié)點(diǎn)鸡典,就如 ChildAChildB 之間的關(guān)系,他們唯一的關(guān)聯(lián)點(diǎn)枪芒,就是擁有相同的父組件彻况。參考之前介紹的兩種關(guān)系的通訊方式谁尸,如果我們向由 ChildAChildB 進(jìn)行通訊,我們可以先通過 ChildAParent 組件進(jìn)行通訊纽甘,再由 ParentChildB 組件進(jìn)行通訊良蛮。

class Parent extends Component {
  state = { msg: 'parent' };

  transferMsg(childAMsg) {
    this.setState({ msg: childAMsg });
  }

  componentDidUpdate() {
    console.log('Parent is update'); // 測(cè)試更新State后哪些組件也被更新了生命周期
  }

  render() {
    return (
      <div>
        <ChildA transferMsg={childAMsg => this.transferMsg(childAMsg)} />
        <ChildB {...this.state} />
      </div>
    );
  }
}

class ChildA extends Component {
  componentDidMount() {
    setTimeout(() => {
      this.props.transferMsg('ChildA');
    }, 1000);
  }

  componentDidUpdate() {
    console.log('ChildA is update'); // 測(cè)試更新State后哪些組件也被更新了生命周期
  }

  render() {
    return <div />;
  }
}

class ChildB extends Component {
  componentDidUpdate() {
    console.log('ChildB update'); // 測(cè)試更新State后哪些組件也被更新了生命周期
  }

  render() {
    return (
      <div>
        <p>I am ChildB, this is ChildA to parent and then to ChildB: {this.props.msg}</p>
        <ChildBchild />
      </div>
    );
  }
}

class ChildBchild extends Component {
  componentDidUpdate() {
    console.log('ChildBchild is update'); // 測(cè)試更新State后哪些組件也被更新了生命周期
  }

  render() {
    return <div />;
  }
}

當(dāng)我們?cè)跒g覽器運(yùn)行時(shí),可以從控制臺(tái)發(fā)現(xiàn)悍赢,各個(gè)組件的 componentDidUpdate 方法均被觸發(fā)背镇。所以,有沒有更好的解決方式呢泽裳?

2. 觀察者模式

觀察者模式也叫發(fā)布-訂閱者模式,發(fā)布者發(fā)布事件破婆,訂閱者監(jiān)聽事件并做出反應(yīng)涮总。我們通過這種模式,在全局定義一個(gè)事件代理管理器祷舀,每一個(gè)組件只需要引入這個(gè)事件代理者即可瀑梗。

事件代理文件,eventBus.js

const eventBus = {
  onObj: {},
  // 事件監(jiān)聽
  on(key, fn) {
    this.onObj[key] === undefined && (this.onObj[key] = []);
    this.onObj[key].push(fn);
  },
  // 事件關(guān)閉
  off(key) {
    this.onObj[key] = [];
    this.oneObj[key] = [];
  },
  // 事件觸發(fā)
  trigger() {
    /*
      備注:
      除了事件參數(shù)裳扯,其他參數(shù)允許傳入數(shù)組抛丽,但對(duì)于傳入的map結(jié)構(gòu)、函數(shù)饰豺、以及多個(gè)其他參數(shù)都沒有做處理亿鲜,
      這點(diǎn)可以根據(jù)個(gè)人需要進(jìn)行拓展
    */
    // 無參返回false
    if (arguments.length === 0) {
      return false;
    }
    // key是事件,args是參數(shù) - 通信信息
    const argumentsArr = [...arguments];
    let key = argumentsArr[0];
    let args = argumentsArr.slice(1);

    if (this.onObj[key] !== undefined && this.onObj[key].length > 0) {
      for (let i in this.onObj[key]) {
        this.onObj[key][i].apply(null, args);
      }
    }
  }
};

export default eventBus;

事件代理文件冤吨,eventBus.js

class Parent extends Component {
  render() {
    return (
      <div>
        <ChildA />
        <ChildB />
      </div>
    );
  }
}

class ChildA extends Component {
  componentDidMount() {
    let hello = 'ChildA - 結(jié)束';
    setTimeout(() => {
      eventBus.trigger('change', hello);
    }, 1000);
  }

  render() {
    return <div></div>;
  }
}

class ChildB extends Component {
  state = {
    msg: 'ChildB - 開始'
  };

  componentDidMount() {
    eventBus.on('change', msg => {
      this.setState({
        msg
      });
    });
  }

  render() {
    return (
      <div>
        <p>ChildA to ChildB component: {this.state.msg}</p>
      </div>
    );
  }
}

3. 關(guān)于Redux與Flux

關(guān)于 ReduxFlux 就是用來管理狀態(tài)和解決組件通信問題的蒿柳。但雖然 Redux 對(duì)于組件間的解耦提供了很大的便利,如果你在考慮該不該使用 Redux 的時(shí)候漩蟆,社區(qū)里有一句話說垒探,“當(dāng)你不知道該不該使用 Redux 的時(shí)候,那就是不需要的”怠李。Redux 用起來一時(shí)爽圾叼,重構(gòu)或者將項(xiàng)目留給后人的時(shí)候,就是個(gè)大坑捺癞,Redux 中的 dispatchsubscribe 方法遍布代碼的每一個(gè)角落夷蚊。雖然 Flux 設(shè)計(jì)中的 Controller-Views 概念就是為了解決這個(gè)問題出發(fā)的,將所有的 subscribe 都置于 Parent 組件(Controller-Views)翘簇,由最上層組件控制下層組件的表現(xiàn)撬码,然而,這不就是我們所說的子組件向父組件通訊這種方式了版保。

參考來源:淘寶-React組件通信原理

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末呜笑,一起剝皮案震驚了整個(gè)濱河市夫否,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌叫胁,老刑警劉巖凰慈,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異驼鹅,居然都是意外死亡微谓,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門输钩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來豺型,“玉大人,你說我怎么就攤上這事买乃∫霭保” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵剪验,是天一觀的道長(zhǎng)肴焊。 經(jīng)常有香客問我,道長(zhǎng)功戚,這世上最難降的妖魔是什么娶眷? 我笑而不...
    開封第一講書人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮啸臀,結(jié)果婚禮上届宠,老公的妹妹穿的比我還像新娘。我一直安慰自己壳咕,他們只是感情好席揽,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著谓厘,像睡著了一般幌羞。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上竟稳,一...
    開封第一講書人閱讀 51,125評(píng)論 1 297
  • 那天属桦,我揣著相機(jī)與錄音,去河邊找鬼他爸。 笑死聂宾,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的诊笤。 我是一名探鬼主播系谐,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了纪他?” 一聲冷哼從身側(cè)響起鄙煤,我...
    開封第一講書人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎茶袒,沒想到半個(gè)月后梯刚,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡薪寓,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年亡资,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片向叉。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡锥腻,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出母谎,到底是詐尸還是另有隱情旷太,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布销睁,位于F島的核電站,受9級(jí)特大地震影響存崖,放射性物質(zhì)發(fā)生泄漏冻记。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一来惧、第九天 我趴在偏房一處隱蔽的房頂上張望冗栗。 院中可真熱鬧,春花似錦供搀、人聲如沸隅居。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)胎源。三九已至,卻和暖如春屿脐,著一層夾襖步出監(jiān)牢的瞬間涕蚤,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來泰國(guó)打工的诵, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留万栅,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓西疤,卻偏偏與公主長(zhǎng)得像烦粒,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子代赁,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容

  • 處理 React 組件之間的交流方式扰她,主要取決于組件之間的關(guān)系兽掰,然而這些關(guān)系的約定人就是你。 React 組件之間...
    落花的季節(jié)閱讀 3,291評(píng)論 0 3
  • 最近學(xué)習(xí)淺嘗則止的學(xué)習(xí)了一下react.js這個(gè)UI的框架义黎,react這個(gè)庫(kù)給我的最大的感覺就是它能夠完全的接管U...
    璀璨天宇閱讀 498評(píng)論 0 0
  • React 開發(fā)模式是組件化開發(fā)禾进, 所以組件間的信息傳遞就尤為重要,React傳遞數(shù)據(jù)的方式主要有3種廉涕。 prop...
    蠻吉大人123閱讀 72評(píng)論 0 0
  • 這里是記錄我在色彩學(xué)習(xí)中的心得泻云,“色”即為“色”,而“澀”為“青澀”狐蜕。代表我不成熟的小建議宠纯。 亞里士多德是眾多學(xué)科...
    SpongeBob被用了閱讀 1,941評(píng)論 0 0
  • 定計(jì)劃這件事,盡管只是偶爾穿插在人生中层释,不一定起作用的分叉路口婆瓜,但是它還是貫穿了我們的一生,一個(gè)家庭主婦對(duì)于每天吃...
    百事可愛喲閱讀 447評(píng)論 0 0