React中需要注意的地方(二)

一、在getInitialState裏邊使用 props 是一種反面模式

提示:這不是React明確規定的,就像經常發生在其它代碼中的那樣;在這種情況下,React能使這種模式更清晰。

    在getInitialState中使用props傳遞的父組件的數據來綁定state,經常會導致所謂的真理的重複來源(就是說可能分不清數據來源了)。只要有可能動態推斷值,以確定不會發生不同步以及維護困難的情況。(小僧翻譯出來也快吐了,原文如果不引入俚語還好,一旦用了就暈乎了,不過在最下方小僧會大致總結下,客官勿急)。

    反面示例:

var MessageBox = React.createClass({
  getInitialState: function() {
    return {nameWithQualifier: 'Mr. ' + this.props.name};
  },

  render: function() {
    return <div>{this.state.nameWithQualifier}</div>;
  }
});

React.render(<MessageBox name="Rogers"/>, mountNode);

    好的示例:

var MessageBox = React.createClass({
  render: function() {
    return <div>{'Mr. ' + this.props.name}</div>;
  }
});

React.render(<MessageBox name="Rogers"/>, mountNode);

 

    然而,如果你明確表示同步不是你的目標,那麼也可以寫成非反向模式。

var Counter = React.createClass({
  getInitialState: function() {
    // naming it initialX clearly indicates that the only purpose
    // of the passed down prop is to initialize something internally
    return {count: this.props.initialCount};
  },

  handleClick: function() {
    this.setState({count: this.state.count + 1});
  },

  render: function() {
    return <div onClick={this.handleClick}>{this.state.count}</div>;
  }
});

React.render(<Counter initialCount={7}/>, mountNode);

    個人總結:getInitialState中初始化的數據,原則是保持在該組件及其子組件使用,不要將父組件的數據(props傳遞過來的數據)使用在這裏。如果你想在該組件內使用的數據需要根據props傳遞過來的數據來初始化,那就是你的數據原型有問題了,爲什麼不在其它方法中直接使用父組件的數據。與父組件通信,最好是通過回調函數,該回調函數是從父組件傳遞過來,並且裏邊封裝了state綁定的數據(即更改的數據)。


二、在組件中使用DOM事件

注意:這裏說的事件是非React提供的事件。React有關於DOM的操作可以很容易和其它類庫合作,如jQuery。

    試着改變窗口大小:

var Box = React.createClass({
  getInitialState: function() {
    return {windowWidth: window.innerWidth};
  },

  handleResize: function(e) {
    this.setState({windowWidth: window.innerWidth});
  },

  componentDidMount: function() {
    window.addEventListener('resize', this.handleResize);
  },

  componentWillUnmount: function() {
    window.removeEventListener('resize', this.handleResize);
  },

  render: function() {
    return <div>Current window width: {this.state.windowWidth}</div>;
  }
});

React.render(<Box />, mountNode);
   componentDidMount 會在組件被渲染之後被調用。在這個方法中通常被用來給DOM添加事件,因爲只有DOM(渲染成功)存在,才能給其添加事件。

      注意這裏的事件是綁定到了React中的組件,而不是最原始的DOM。React會通過一個叫 autobinding 的過程將這個事件方法綁定到組件中。


三、用ajax加載初始數據

    在componentDidMount中獲取數據:當響應到達時,會將數據存儲的state中,然後觸發更新UI。

    當處理異步請求的響應時,要確定當前組件是否仍然在更新中,通過 this.isMounted() 來判斷。

下邊的例子是獲取所需的Github用戶最新動態:

 

var UserGist = React.createClass({
  getInitialState: function() {
    return {
      username: '',
      lastGistUrl: ''
    };
  },

  componentDidMount: function() {
    $.get(this.props.source, function(result) {
      var lastGist = result[0];
      if (this.isMounted()) {
        this.setState({
          username: lastGist.owner.login,
          lastGistUrl: lastGist.html_url
        });
      }
    }.bind(this));
  },<pre name="code" class="javascript">React.render(<div id={false} />, mountNode);

render: function() { return ( <div> {this.state.username}'s last gist is <a href={this.state.lastGistUrl}>here</a>. </div> ); }});React.render( <UserGist source="https://api.github.com/users/octocat/gists" />, mountNode);


注意:例子中的bind(this),只能用在函數

四、在JSX中使用false

    下邊是不同情況系 false 渲染的過程:

    1. 渲染成id=“false”:

React.render(<div id={false} />, mountNode);
    2. 渲染成輸入框中的字符串值
React.render(<input value={false} />, mountNode);
    3. 沒有子元素的情況
React.render(<div>{false}</div>, mountNode);
在這種情況中,false被渲染成<div></div>之間的文本,而是作爲一個div的子元素,是爲了可以有更多靈活的用法:如
<div>{x > 1 && 'You have more than one item'}</div>

五、組件間的通信

    在父子組件之間的通信,可以簡單的通過props。

    而在同級子組件之間的通信:假如你的 GroceryList 組件有很多通過數組生成的條目。當其中一個條目被點擊時,你想要展示該條目的名稱。

var GroceryList = React.createClass({
  handleClick: function(i) {
    console.log('You clicked: ' + this.props.items[i]);
  },

  render: function() {
    return (
      <div>
        {this.props.items.map(function(item, i) {
          return (
            <div onClick={this.handleClick.bind(this, i)} key={i}>{item}</div>
          );
        }, this)}
      </div>
    );
  }
});

React.render(
  <GroceryList items={['Apple', 'Banana', 'Cranberry']} />, mountNode
);
注意bind(this, arg1, arg2, ...)的用法:這裏我們只是去傳遞更多的參數給 handleClick 方法。這並不是React新加的規定,而是JavaScript就有的。

    在兩個同級子組件之間的通信,你可以設置一個全局的事件componentWillUnmount。在 componentDidMount 中註冊監聽,在componentWillUnmount 卸載監聽,當監聽到一個事件時,調用setState()更新視圖。


六、暴露組件的功能

    組件之間的通信也有一個不常見的方法:可以簡單的在子組件中調用父組件的回調函數,此處的子組件拆分更加瑣碎。

    下邊的例子中,每點擊一個條目然後自身被刪除,當僅剩一個條目時,就爲其加上動畫效果。

var Todo = React.createClass({
  render: function() {
    return <div onClick={this.props.onClick}>{this.props.title}</div>;
  },

  //this component will be accessed by the parent through the `ref` attribute
  animate: function() {
    console.log('Pretend %s is animating', this.props.title);
  }
});

var Todos = React.createClass({
  getInitialState: function() {
    return {items: ['Apple', 'Banana', 'Cranberry']};
  },

  handleClick: function(index) {
    var items = this.state.items.filter(function(item, i) {
      return index !== i;
    });
    this.setState({items: items}, function() {
      if (items.length === 1) {
        this.refs.item0.animate();
      }
    }.bind(this));
  },

  render: function() {
    return (
      <div>
        {this.state.items.map(function(item, i) {
          var boundClick = this.handleClick.bind(this, i);
          return (
            <Todo onClick={boundClick} key={i} title={item} ref={'item' + i} />
          );
        }, this)}
      </div>
    );
  }
});

React.render(<Todos />, mountNode);

    或者,你也可以通過在子組件 todo 中定義一個 isLastUnfinishedItem 屬性,然後在 componentDidUpdate 進行判斷,是否添加動畫效果。然而,當有多個屬性來控制動畫時就顯得混亂了。

七、組件引用

    當你在一個大的非React應用中想要使用React的組件或者將你的代碼過渡到React,你需要想辦法引用組件。React.render 方法返回的就是一個完整組件的引用。

var myComponent = React.render(<MyComponent />, myContainer);

    要記住,JSX語法並不會返回一個組件實例。它只是一個React元素:一個組件的輕量級表現方式。

var myComponentElement = <MyComponent />; // This is just a ReactElement.

// Some code here...

var myComponentInstance = React.render(myComponentElement, myContainer);
  注意:這裏只能在頂級作用域(組件外部)中使用。在組件內部應該使用 props 和 state 來保持組件間的通信。

   

八、this.props.children 未定義

     你不能使用this.props.children來訪問組件的子節點。this.props.children被設計用來表示自身的節點。

var App = React.createClass({
  componentDidMount: function() {
    // This doesn't refer to the `span`s! It refers to the children between
    // last line's `<App></App>`, which are undefined.
    console.log(this.props.children);
  },

  render: function() {
    return <div><span/><span/></div>;
  }
});

React.render(<App></App>, mountNode);
  想要訪問子節點,就要使用在span中ref


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章