React 組件之間如何交流

來源:互聯網
上載者:User

標籤:工具   publish   any   nbsp   中介   tor   控制   target   lob   

原文  http://blog.51yuekan.com/2015/07/23/2015-07-19-react-component-communicate/主題 React 前言

今天群裡面有很多都在問關於 React 組件之間是如何通訊的問題,之前自己寫的時候也遇到過這類問題。下面是我看到的一篇不錯英文版的翻譯,看過我部落格的人都知道,我翻譯可能不會按部就班,會儘可能用中文的意思,來將作者要講述的技術描述清楚。英文能力有限,如果有不對的地方請跟我留言,一定修改……^_^

原著序

處理 React 組件之間的交流方式,主要取決於組件之間的關係,然而這些關係的約定人就是你。

我不會講太多關於 data-stores、data-adapters 或者 data-helpers 之類的話題。我下面只專註於 React 組件本身的交流方式的講解。

React 組件之間交流的方式,可以分為以下 3 種:

  • 【父組件】向【子組件】傳值;
  • 【子組件】向【父組件】傳值;
  • 沒有任何嵌套關係的組件之間傳值(PS:比如:兄弟組件之間傳值)
一、【父組件】向【子組件】傳值

初步使用

這個是相當容易的,在使用 React 開發的過程中經常會使用到,主要是利用 props 來進行交流。例子如下:

// 父組件var MyContainer = React.createClass({  getInitialState: function () {    return {      checked: true    };  },  render: function() {    return (      <ToggleButton text="Toggle me" checked={this.state.checked} />    );  }});// 子組件var ToggleButton = React.createClass({  render: function () {    // 從【父組件】擷取的值    var checked = this.props.checked,        text = this.props.text;    return (        <label>{text}: <input type="checkbox" checked={checked} /></label>    );  }});

進一步討論

如果組件嵌套層次太深,那麼從外到內組件的交流成本就變得很高,通過 props 傳遞值的優勢就不那麼明顯了。(PS:所以我建議儘可能的減少組件的層次,就像寫 HTML 一樣,簡單清晰的結構更惹人愛)

// 父組件var MyContainer = React.createClass({  render: function() {    return (      <Intermediate text="where is my son?" />    );  }});// 子組件1:中間嵌套的組件var Intermediate = React.createClass({  render: function () {    return (      <Child text={this.props.text} />    );  }});// 子組件2:子組件1的子組件var Child = React.createClass({  render: function () {    return (      <span>{this.props.text}</span>    );  }});
二、【子組件】向【父組件】傳值

接下來,我們介紹【子組件】控制自己的 state 然後告訴【父組件】的點擊狀態,然後在【父組件】中展示出來。因此,我們添加一個 change 事件來做互動。

// 父組件var MyContainer = React.createClass({  getInitialState: function () {    return {      checked: false    };  },  onChildChanged: function (newState) {    this.setState({      checked: newState    });  },  render: function() {    var isChecked = this.state.checked ? ‘yes‘ : ‘no‘;    return (      <div>        <div>Are you checked: {isChecked}</div>        <ToggleButton text="Toggle me"          initialChecked={this.state.checked}          callbackParent={this.onChildChanged}          />      </div>    );  }});// 子組件var ToggleButton = React.createClass({  getInitialState: function () {    return {      checked: this.props.initialChecked    };  },  onTextChange: function () {    var newState = !this.state.checked;    this.setState({      checked: newState    });    // 這裡要注意:setState 是一個非同步方法呼叫,所以需要操作緩衝的當前值    this.props.callbackParent(newState);  },  render: function () {    // 從【父組件】擷取的值    var text = this.props.text;    // 組件自身的狀態資料    var checked = this.state.checked;    return (        <label>{text}: <input type="checkbox" checked={checked}                 onChange={this.onTextChange} /></label>    );  }});

我覺得原文作者用代碼不是很直觀,接下來我話一個流程走向簡圖來直觀描述一下這個過程:

這樣做其實是依賴 props 來傳遞事件的引用,並通過回調的方式來實現的,這樣實現不是特別好,但是在沒有任何工具的情況下也是一種簡單的實現方式

這裡會出現一個我們在之前討論的問題,就是組件有多層嵌套的情況下,你必須要一次傳入回呼函數給 props 來實現子組件向父組件傳值或者操作。

Tiny-Tip: React Event System

在 onChange 事件或者其他 React 事件中,你能夠擷取以下東西:

  • 【this】:指向你的組件
  • 【一個參數】:這個參數是一個 React 合成事件 ,SyntheticEvent。

React 對所有事件的管理都是自己實現的,與我們之前使用的 onclick、onchange 事件不一樣。從根本上來說,他們都是綁定到 body 上。

document.on(‘change‘, ‘input[data-reactid=".0.2"]‘, function () {...});

上面這份代碼不是來自於 React,只是打一個比方而已。

如果我沒有猜錯的話,React 真正處理一個事件的代碼如下:

var listenTo = ReactBrowserEventEmitter.listenTo;...function putListener(id, registrationName, listener, transaction) {  ...  var container = ReactMount.findReactContainerForID(id);  if (container) {    var doc = container.nodeType === ELEMENT_NODE_TYPE ? container.ownerDocument : container;    listenTo(registrationName, doc);  }  ...}// 在監聽事件的內部,我們能發現如下:target.addEventListener(eventType, callback, false);

這裡有所有 React 支援的事件: 中文文檔-事件系統

多個子組件使用同一個回調的情況

// 父組件var MyContainer = React.createClass({  getInitialState: function () {    return {      totalChecked: 0    };  },  onChildChanged: function (newState) {    var newToral = this.state.totalChecked      + (newState ? 1 : -1);    this.setState({      totalChecked: newToral    });  },  render: function() {    var totalChecked = this.state.totalChecked;    return (      <div>        <div>How many are checked: {totalChecked}</div>        <ToggleButton text="Toggle me"          initialChecked={this.state.checked}          callbackParent={this.onChildChanged}          />        <ToggleButton text="Toggle me too"            initialChecked={this.state.checked}            callbackParent={this.onChildChanged}          />        <ToggleButton text="And me"          initialChecked={this.state.checked}          callbackParent={this.onChildChanged}          />      </div>    );  }});// 子組件var ToggleButton = React.createClass({  getInitialState: function () {    return {      checked: this.props.initialChecked    };  },  onTextChange: function () {    var newState = !this.state.checked;    this.setState({      checked: newState    });    // 這裡要注意:setState 是一個非同步方法呼叫,所以需要操作緩衝的當前值    this.props.callbackParent(newState);  },  render: function () {    // 從【父組件】擷取的值    var text = this.props.text;    // 組件自身的狀態資料    var checked = this.state.checked;    return (        <label>{text}: <input type="checkbox" checked={checked} onChange={this.onTextChange} /></label>    );  }});

這是非常容易理解的,在父組件中我們增加了一個【totalChecked】來替代之前例子中的【checked】,當子組件改變的時候,使用同一個子組件的回呼函數給父組件傳回值。

三、沒有任何嵌套關係的組件之間傳值

如果組件之間沒有任何關係,組件嵌套層次比較深(個人認為 2 層以上已經算深了),或者你為了一些組件能夠訂閱、寫入一些訊號,不想讓組件之間插入一個組件,讓兩個組件處於獨立的關係。對於事件系統,這裡有 2 個基本操作步驟:訂閱(subscribe)/監聽(listen)一個事件通知,並發送(send)/觸發(trigger)/發布(publish)/發送(dispatch)一個事件通知那些想要的組件。

下面講介紹 3 種模式來處理事件,你能 點擊這裡 來比較一下它們。

簡單總結一下:

(1) Event Emitter/Target/Dispatcher

特點:需要一個指定的訂閱源

// to subscribeotherObject.addEventListener(‘click’, function() { alert(‘click!’); });// to dispatchthis.dispatchEvent(‘click’);

(2) Publish / Subscribe

特點:觸發事件的時候,你不需要指定一個特定的源,因為它是使用一個全域對象來處理事件(其實就是一個全域廣播的方式來處理事件)

// to subscribeglobalBroadcaster.subscribe(‘click’, function() { alert(‘click!’); });// to dispatchglobalBroadcaster.publish(‘click’);

(3) Signals

特點:與Event Emitter/Target/Dispatcher相似,但是你不要使用隨機的字串作為事件觸發的引用。觸發事件的每一個對象都需要一個確切的名字(就是類似寫入程式碼類的去寫事件名字),並且在觸發的時候,也必須要指定確切的事件。(看例子吧,很好理解)

// to subscribeotherObject.clicked.add(function() { alert(‘click’); });// to dispatchthis.clicked.dispatch();

如果你只想簡單的使用一下,並不需要其他動作,可以用簡單的方式來實現:

// 簡單實現了一下 subscribe 和 dispatchvar EventEmitter = {    _events: {},    dispatch: function (event, data) {        if (!this._events[event]) { // 沒有監聽事件          return;        }        for (var i = 0; i < this._events[event].length; i++) {            this._events[event][i](data);        }    },    subscribe: function (event, callback) {      // 建立一個新事件數目組      if (!this._events[event]) {        this._events[event] = [];      }      this._events[event].push(callback);    }};otherObject.subscribe(‘namechanged‘, function(data) { alert(data.name); });this.dispatch(‘namechanged‘, { name: ‘John‘ });

如果你想使用 Publish/Subscribe 模型,可以使用: PubSubJS

React 團隊使用的是: js-signals 它基於 Signals 模式,用起來相當不錯。

Events in React

使用 React 事件的時候,必須關注下面兩個方法:

componentDidMountcomponentWillUnmount

在處理事件的時候,需要注意:

在 componentDidMount 事件中,如果組件掛載(mounted)完成,再訂閱事件;當組件卸載(unmounted)的時候,在 componentWillUnmount 事件中取消事件的訂閱。

(如果不是很清楚可以查閱 React 對生命週期介紹的文檔,裡面也有描述。原文中介紹的是 componentWillMount 個人認為應該是掛載完成後訂閱事件,比如Animation這個就必須掛載,並且不能動態添加,謹慎點更好)

因為組件的渲染和銷毀是由 React 來控制的,我們不知道怎麼引用他們,所以EventEmitter 模式在處理組件的時候用處不大。

pub/sub 模式可以使用,你不需要知道引用。

下面來一個例子:實現有多個 product 組件,點擊他們的時候,展示 product 的名字。

(我在例子中引入了之前推薦的 PubSubJS 庫,如果你覺得引入代價太大,也可以手寫一個簡版,還是比較容易的,很好用哈,大家也可以體驗,但是我還是不推薦全域廣播的方式)

// 定義一個容器var ProductList = React.createClass({    render: function () {      return (        <div>          <ProductSelection />          <Product name="product 1" />          <Product name="product 2" />          <Product name="product 3" />        </div>      );    }});// 用於展示點擊的產品資訊容器var ProductSelection = React.createClass({  getInitialState: function() {    return {      selection: ‘none‘    };  },  componentDidMount: function () {    this.pubsub_token = PubSub.subscribe(‘products‘, function (topic, product) {      this.setState({        selection: product      });    }.bind(this));  },  componentWillUnmount: function () {    PubSub.unsubscribe(this.pubsub_token);  },  render: function () {    return (      <p>You have selected the product : {this.state.selection}</p>    );  }});var Product = React.createClass({  onclick: function () {    PubSub.publish(‘products‘, this.props.name);  },  render: function() {    return <div onClick={this.onclick}>{this.props.name}</div>;  }});

ES6: yield and js-csp

ES6 中有一種傳遞資訊的方式,使用產生函數(generators)和 yield 關鍵字。可以看一下 https://github.com/ubolonton/js-csp.

(這裡我寫一個簡單的 DEMO 介紹一下這種新的傳遞方式,其實大同小異)

function* list() {for(var i = 0; i < arguments.length; i++) {yield arguments[i];}return "done.";}var o = list(1, 2, 3);var cur = o.next;while(!cur.done) {cur = o.next();console.log(cur);}

以上例子來自於屈屈的一篇部落格: ES6 中的產生器函數介紹 屈屈是一個大牛,大家可以經常關注他的部落格。

通常來說,你有一個隊列,對象在裡面都能找到一個引用,在定義的時候鎖住,當發生的時候,立即開啟鎖執行。js-csp 是一種解決辦法,也許以後還會有其他解決辦法。

結尾

在實際應用中,按照實際要解決的需求選擇解決辦法。對於小應用程式,你可以使用 props 和回調的方法進行組件之間的資料交換。你可以通過 pub/sub 模式,以避免汙染你的組件。在這裡,我們不是在談論資料,只是組件。對於資料的請求、資料的變化等情境,可以使用 Facebook 的 Flux、Relay、GraphQL 來處理,都非常的好用。

文中的每一個例子我都驗證過了,主要使用最原始的引入檔案方式,建立服務使用的 http-server 包,大家也可以嘗試自己來一次。

React 組件之間如何交流

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.