React 官方文檔翻譯一

GUIDS

第一章 為什么使用React?

React

  • 一個提供了用戶接口的JavaScript庫。
  • 誕生于Facebook和Instagram項目中。
  • 許多人把它看做是MVC編程模式。
    我們編寫React只為解決一件事:數據需要實時刷新的大型應用程序

簡單

當相關數據發生改變時,React會自動更新所有的UI組件。你可以很簡單地掌控不同狀態下的app

聲明式

當數據改變時,React就好像被點擊了更新按鈕一樣,知道如何更新需要改變的部分

建立復用組件

React都是為了建立可以重復利用的組件。實際上,你用React可以做的唯一一件事就是建立組件。因為代碼都是封裝起來的,組件讓代碼重復利用,方便測試,并且便于單獨考慮每個組件機制。

給它5分鐘

React挑戰了許多傳統的想法,你第一次看到這種想法可能覺得它很瘋狂Give it five minutes。 當閱讀這篇指南時,這些瘋狂的想法已經在FB和Instagram還有其他網站上建立了上千個組件了。

學習更多

link.

第二章 展示數據

你能用UI做的最基本的事情就是顯示數據。React讓你很簡單地展示數據,并且讓用戶界面自動地更新這些數據。

開始吧~

先讓我們看個例子: Hello-react.html

    <script src="https://unpkg.com/react@15.3.2/dist/react.js"></script> 
    <script src="https://unpkg.com/react-dom@15.3.2/dist/react-dom.js"></script> 
    <script src="https://unpkg.com/babel-core@5.8.38/browser.min.js"></script>
    <script type="text/babel"> //
         **寫在這里 ** 
    </script>

    var HelloWorld = React.createClass({ 
          render: function() { 
              return ( 
                  <p> 
                          Hello, <input type="text" placeholder="Your name here" />! 
                          It is {this.props.date.toTimeString()} 
                  </p> 
              ); 
           }
      });
      
     setInterval(function() { 
            ReactDOM.render( 
                    <HelloWorld date={new Date()} />, 
                   document.getElementById('example') 
            );
     }, 500);

Reactive 更新

hello-react.html
這個頁面,當你在input輸入字符時,react自動更改為時間,即使你現在沒寫任何代碼,React自動給你完成了。它是怎么做到的呢?除非特殊需要,React不會直接操縱Dom本身,它先在內部模擬的DOM身上執行這些變化,為你計算出最有效的改變方式,再應用到實際DOM中。

傳入組件的參數叫做props—— properties的簡稱。他們是通過JSX語法傳入的。你可以把他們當做組件的常量(不可變的量),也就是說,不要更改this.props

Comonets組件就像Functions
React組件非常簡單。你可以把他們看作是簡單的functions,接受參數props和state,再渲染出HTML。這樣想更能幫助你。

一個限制:React組件只能渲染一個單獨的節點(譯者注:就是說所有的東西要包在一個div里,或者別的tag里面)。如果你想返回多個節點,他們必須報在一個單獨的根節點中。

JSX語法

我們強烈認為,組件是分離模塊正確的方式,而不是模版或者邏輯地展示(display logic)。我們認為標記和代碼要密切結合在一起。另外,邏輯地展示經常很復雜,并且用模版語言會讓代碼變得笨重。

我們發現最好的解決方法就是用Javascript直接生成HTML和組件樹,這樣你就用真正的編程語言來建立UI。

為了更簡單的說明,我們增加了一個更簡單的像HTML的語法來創建這些React樹節點。JSX可以讓你通過HTML語法創建JavaScript對象。在React中生成一個鏈接的JavaScript語法是

    React.createElement('a', {href: 'https://facebook.github.io/react/'}, 'Hello!')

使用JSX的話,你就可以直接這樣寫

    <a >Hello!</a>

我們發現這讓開發React app開發更簡單,設計師更喜歡這種語法,但是每個人有自己的工作流,所以JSX不是React開發所必須的
JSX很小,學習更多請看 JSX in depth(https://facebook.github.io/react/docs/jsx-in-depth.html)?;蛘卟榭?the Babel REPL.

JSX和HTML很像,但是不完全一樣。查看他們有什么不同 [JSX gotchas] (https://facebook.github.io/react/docs/jsx-gotchas.html)。[Babel揭示了許多如何使用JSX的方法] (http://babeljs.io/docs/setup/), 包括Ruby on Rails的命令行工具。

沒有JSX的React

JSX完全是可選的,你可以不適用它。完全用JavaScript創建React元素你要使用React.createElement, 它接收標簽名或者組件作為參數,還有很多可選的子變量。

   var child1 = React.createElement('li', null, 'First Text Content');
   var child2 = React.createElement('li', null, 'Second Text Content');
   var root = React.createElement('ul', { className: 'my-list' }, child1, child2);
   ReactDOM.render(root, document.getElementById('example'));

為了方便,你可以創建簡寫的工廠函數

  var Factory = React.createFactory(ComponentClass);
  ...
  var root = Factory({ custom: 'prop' });
  ReactDOM.render(root, document.getElementById('example'));

對于常用的HTML標簽,React已經有內置的factories

  var root = React.DOM.ul({ className: 'my-list' }, React.DOM.li(null, 'Text Content') );

2.1 JSX in Depth

JSX is 是一個JavaScript語義延伸,看起來像XML。你可以用簡單的JSX語義書寫React

[此處省略原因和比較](https://facebook.github.io/react/docs/jsx-in-depth.html) 

組件命名空間

如果你正在建立一個有很多子組件的組件,例如一個表單,你可以會有很多很多的變量聲明:

// Awkward block of variable declarationsvar 
Form = MyFormComponent;
var FormRow = Form.Row;
var FormLabel = Form.Label;
var FormInput = Form.Input;
var App = ( 
    <Form> 
      <FormRow> 
      <FormLabel />
      <FormInput /> 
      </FormRow> 
    </Form>
);

為了讓它更簡便,命名空間*應運而生,他可以讓你使用組件的時候,可以用其他組件作為屬性。

你還需要聲明:

  var MyFormComponent = React.createClass({ ... });
  MyFormComponent.Row = React.createClass({ ... });
  MyFormComponent.Label = React.createClass({ ... });
  MyFormComponent.Input = React.createClass({ ... });

JSX 會在編譯的時候自動handle這些。

JavaScript 表達式

Attribute 表達式

如果要使用JavaScript 表達式作為屬性值,要把他們包在{ } 花括號中,而不是“ ”引號中

// Input (JSX):
var person = <Person name={window.isLoggedIn ? window.name : ''} />;
// Output (JS):
var person = React.createElement( 
    Person, {name: window.isLoggedIn ? window.name : ''}
);
Boolean 屬性

在JSX中沒有設置屬性的值則默認為true, 設置了屬性后才會視為false。這個問題時常出現在使用HTML中的disabled, required, checked, readOnly屬性的時候。

    // 二者相同
    <input type="button" disabled />;
    <input type="button" disabled={true} />;

   // 二者相同
    <input type="button" />;
    <input type="button" disabled={false} />;
Child Expressions 子表達式

JavaScript表達式也可以用在children上

    // Input (JSX):
    var content = <Container>{window.isLoggedIn ? <Nav /> : <Login />}</Container>;
注釋

給你JSX添加注釋很簡單,他們只是JS的語法。你要包在{ }里面

var content = ( 
    <Nav>
         {/* child comment, put {} around */} 
            <Person /* multi line comment */ 
                  name={window.isLoggedIn ? window.name : ''}  // end of line comment /> 
    </Nav>
);

2.2 JSX 延展屬性

如果你提前知道所有你想放在組件的所有屬性,就可以方便的使用JSX

  var component = <Component foo={x} bar={y} />;
多個Props是不好的

如果你提前不知道你想設置的屬性,你可能想要在之后把他們添加到一個對象

  var component = <Component />; 
  component.props.foo = x; // bad 
  component.props.bar = y; // also bad

這是一種反模式,因為它意味著我們不能幫你檢查propTypes的正確性,這導致你的propTypes之后會報錯,模糊的堆疊追蹤???(a cryptic stack trace)。Props應該是不變的,在某處改變props對象會造成無法預期的結果。所以此刻把它看成一個凍結的(不可更改的)對象。

延展屬性

現在你可以使用一個新的JSX特性,叫spread attributes

var props = {}; 
props.foo = x; 
props.bar = y; 
var component = <Component {...props} />;

你可以傳遞并且拷貝組件的props的屬性,你可以多次使用,結合其他屬性。聲明的順序很重要,后申明的屬性會覆蓋之前的。

var props = { foo: 'default' }; 
var component = <Component {...props} foo={'override'} />;            
console.log(component.props.foo); // 'override'
奇怪的...是什么?

...是ES6的語法,我們支持這些語法來提供更簡潔的JSX

2. 3 JSX Gotchas 性能和可伸縮性

JSX看起來像HTML但是有些重要的區別你需要知道

DOM的區別

為了實現跨平臺的統一,React完成了獨立于瀏覽器的事件和DOM系統。我們借機清掃了一些DOM未完善的地方。所有的DOM屬性(包括事件處理 event handler)都是駝峰命名,與JavaScript style一致。這里我們故意地打破了規則,因為它是前后矛盾的。然而,data-和aria-屬性 conform to the specs只是用小寫。Style屬性接受一個駝峰法明明的JavaScript對象,而不是一個CSS字符串,這和JavaScript DOM style是一致的,并且能防止XSS安全漏洞。因為class和for都是JavaScript的保留字, JSX元素內置了DOM nodes DOM節點,應該使用className和htmlFor。自定義的元素可以使用class和for(eg.<my-tag class="foo" />。所有的事件對象遵照W3C規則,所有的events(包括submit)bubble correctly per the W3C spec. See Event System for more details.
The onChange事件就像你期待的那樣,無論是form field更改了,event被觸發而不是on blur. 我們有意地更改了這個瀏覽器的默認表現因為onChange表現不當,React依賴這個事件來即使地響應用戶輸入。更多查看 FormsForm input屬性,例如value和checked,還有textarea。 More here.

HTML Entities 實體

你可以在JSX中插入HTML實體

  <div>First · Second</div>

如果你想要動態地顯示HTML內容,你陷入double escaping問題,因為為了防止XSS漏洞攻擊, React默認跳過要顯示的字符串(譯者注:翻譯的好爛,這里說的是轉義字符的問題,直接用就顯示成字符串了,看例子)

  // Bad: It displays "First · Second"
  <div>{'First · Second'}</div>

有很多方法可以解決這個問題。最簡單的就是直接用JavaScript寫Unicode字符,你需要確保文件是用UTF-8保存的,并且瀏覽器設置了UTF-8編碼。

<div>{'First · Second'}</div>

一個更安全的方法是找到 unicode對應的數字 在JavaScript字符串里使用它

<div>{'First \u00b7 Second'}</div>
<div>{'First ' + String.fromCharCode(183) + ' Second'}</div>

你可以使用字符串和JSX元素的混合數組,每個數組的JSX元素需要一個唯一的key鍵值

<div>{['First ', <span key="middot">·</span>, ' Second']}</div>

最后一種方法,你總有辦法插入原生的HTML代碼 insert raw HTML.

<div dangerouslySetInnerHTML={{__html: 'First · Second'}} />

自定義HTML屬性

如果你要給HTML元素傳遞自定義的屬性,React默認不會渲染它,你要加個前綴data-

<div data-custom-attribute="foo" />

但是呢,自定義的組件中,用連字符-連接的自定義屬性都支持

<x-my-component custom-attribute="foo" />

網絡無障礙 屬性aria-* 會被好好的渲染

<div aria-hidden={true} />

第三章 交互性和動態的UIs

你已經學會了如何用React展示數據 learned how to display data 現在看看如何交互UI組件

簡單例子

class likeButton extends React.Component{
    constructor() { 
        super(); 
        this.state = { liked: false }; 
        this.handleClick = this.handleClick.bind(this); 
    }

    handleClick() { 
        this.setState({liked: !this.state.liked}); 
    }

    render() { 
        const text = this.state.liked ? 'liked' : 'haven\'t liked'; 
        return ( 
            <div onClick={this.handleClick}> You {text} this. Click to toggle. </div> 
        ); 
    }
}

事件驅動和合成事件 Event Handling and Synthetic Events

React,可以把event handler作為prop傳遞進來,就像HTML那樣。React保證所有的事件在不同瀏覽器中有同樣的效果。React知道如何根據規范bubble和捕獲event事件,events被傳遞到你的event handler中,保證了不同瀏覽器中的一致性 the W3C spec

系統內部:自動綁定和事件委托

系統內部,React讓你的代碼易讀懂并且高性能

自動綁定Autobinding當創建JavaScript的callback函數時,你常常需要bind方法來綁定this,好讓正確的this變量傳入。React中每個方法都會自動綁定當前的組件變量(除非你使用ES6語法)。React會緩存綁定方法,可以提高CPU和內存的效力。還能讓你少打字。

事件委托Event delegationReact并不是真的把event handlers綁定到了節點node本身。當React啟動時,它先用一個event listener在最頂層top level監聽所有的events,當一個組件被掛載或者卸載,event handlers就相應地增加或刪除掉一個內部的映射mapping。當event發生,React知道如何利用這個mapping去派送它。當映射庫mapping中沒有event handlers時,React就執行空操作no-ops。(譯者注,就像老舊的電話接線機似的,接線員在最頂層,看到有個組件打電話進來了,它就根據線路圖傳送過去,組件掛掉電話時,它就把線路掐斷)如果你想了解為什么它如此高效:see David Walsh's excellent blog post.

組件和公平的(?Just)狀態機

React認為UIs都是狀態機。UI有多鐘不同的狀態,只需渲染這些不同的狀態就能很好地呈現你的UI。

React中,你只需要更新一個組件的狀態,然后根據這個新狀態render一個新的UI。React會高效率地自動為你更新DOM

State 狀態機是如何工作的

一個常見的方法就是用setState(data, callback)通知React數據已經改變了,這個方法會把新數據合并到當前狀態this.state,再重新渲染組件,當組件渲染完成,callback方法被調用。大部分時候你根本不用寫callback方法,因為React會好好為你更新UI的。

什么組件需要用到State呢?

你的大多數組件只是從props讀取數據再進行渲染。然而,有時你需要獲取input的值,一個server的請求或者一段時間。這種情況下你要用state

盡量讓你的組件避免使用state這樣做你可以保證state獨立的邏輯性,并且減少信息冗余。

一個常見的模式是,創建幾個不用state的組件來旋繞數據,然后在這基礎上創建一個state組件,將state這個參數通過props傳遞給他們。State組件封裝了所有需要交互的邏輯,非state組件負責渲染數據。

在state什么該做?

State應該包含數據,組件的event handlers可以改變這些數據,并更新UI。在真實的apps中,數據可能是很小的JSON串。當創建state組件時,考慮如何最小化地展示這個state,只在this.state里儲存必要的數據,基于這個數據計算出其他需要的信息。你會發現這樣思考后書寫出來的程序可以創造出最正確,因為給state增加的冗余的計算值會導致在同步時存儲他們,而不是依賴React組件去計算。

在state什么不該做?

this.state應該只包含需要呈現在UI上的極少的數據,它不應該包含:
計算出的數據。不要擔心根據state計算得到的值——如果你的計算都在render()中完成,更能保證你的UI是一致的。例如:如果你保存了一個list在state中,你想要render它的長度的字符串形式,只要render()中使用this.state.listItems.length+'list items' 方法,而不是把這個結果儲存在state中去調用

React組件根據props和state在render()中創建

props中的重復數據如果可能的話,嘗試用props作為數據來源。一個有效的使用就是在state中儲存props,這樣你就能知道它之前的值,因為props可能會根據父組件的渲染結果而改變。

多個組件

目前我們已經知道了如何書寫一個單獨的組件來戰士數據,并且處理用戶輸入。接下來讓我們看看React最出色的特性:可組合性。

Motivation: Separation of Concerns

動機:分離關注點

通過創建模塊化的組件,可以復用有完善接口的組件,就像使用fuctions和classes一樣,你能從模塊化組件受益多多。特別是,通過創建新組建可以分離app的關注點。通過給你的程序創建個性化組化庫,你能找到更適合更新UI的方式。

復合組件案例

讓我們創建一個簡單的Avatar組件,用來展示Facebook頁面的圖片和名字。這個案例調用了Facebook Graph API

所有者

上面這個例子,Avatar的own示例是PagePic和PageLink。React中,一個所有者就是給其他組件設置props的組件。更正式的說,如果組件Y的render中,創建了組件X,我們就說X被Y擁有。就像之前討論的,一個組件不能改變它的props,他們會一直和owner所有者設置的值一致。這個基礎不變量會保證UI的一致性。
區分owner-ownee主人-奴隸的關系、父-子的關系是很重要的,React中主奴的關系很明確,父子的關系就像DOM一樣。上述的例子中,Avatar奴役div,pagePic和PageLink,div是PagePic和PageLink的爸爸。

當你創建了一個React組件示例時,你可以在{ }包含額外的React組件或者JavaScript表達式

 <Parent><Child /></Parent>

父Parent可以通過this.porps.children讀取children子的內容。this.props.children是一個特殊的數據結構,調用了 React.Children utilities 來操縱他們。

Child Reconciliation

Reconciliation是React用每個render更新DOM的過程。通常來說,子組件根據他們render的順序reconcil。例如,假設兩個render傳遞了以下的markup

// Render Pass 1
<Card> 
    <p>Paragraph 1</p> 
    <p>Paragraph 2</p>
</Card>
// Render Pass 2
<Card> 
        <p>Paragraph 2</p>
</Card>

可以直觀的看出,<p>Paragraph 1</p>被去掉了。React改變第一個child的內容來更新DOM,并且destroy的最后一個child。React更具children的順序reconciles

擁有state的Children

對于大多數組件來說,這不是什么大問題。然而,對于有state的組件來說,攜帶著this.state.保存的data,進行render,就很有問題。

大多數情況下,這些可以通過隱藏元素而不是destroy元素來規避。

// Render Pass 1
<Card> 
    <p>Paragraph 1</p> 
    <p>Paragraph 2</p>
</Card>
// Render Pass 2
<Card> 
    <p style={{display: 'none'}}>Paragraph 1</p> 
    <p>Paragraph 2</p>
</Card>

動態的Children

這種情況就更復雜了。當children被攪亂了(因為搜索結果??)或者,如果新的組件被添加到list的前面(在stream中)。這些情況下,通過render的每個child的identity和state都必須保持,你可以給每個孩子分配個獨特的key

 render: function() { 
    var results = this.props.results; 
    return ( 
        <ol> {  results.map(function(result) { 
                    return <li key={result.id}>{result.text}</li>; })
                } 
        </ol> 
    ); 
}

當React reconciles有key的children時,它會確保所有的key都儲存起來。key必須直接在組件的數組中提供,而不是在包含的HTML children組件的容器上。(譯者注:不是綁在li上,是綁在包含li的組件上)

數據流

React中,主人的數據流通過props從主人向奴隸組件傳遞。這是高效的單方向數據捆綁:主人在props上捆綁奴隸需要的數據,主人根據props或者state進行計算。因為這個過程遞歸地進行,數據會自動更新。

性能上需要注意的是

你可能認為如果一個主任好多個奴隸節點,要更新一次數據很奢侈。好消息就是JavaScript很高效,render()方法又很簡單,素有大多數程序這個過程會非??臁A硗?,瓶頸總是發生在DOM的改變,而不是JS的遞歸。React會最優化批處理和改變檢測。然而,有時你真的想對性能更精細地掌控。這時你需要重寫override shouldComponentUpdate()。要 return false當你想要React跳過處理subtree。See the React reference docs for more information.

注意:
如果 shouldComponentUpdate() returns false 當數據發生改變時,React不能同步更新UI。請確保你知道你在干什么,只用在當你發現性能問題的時候,不要低估JavaScript更新DOM的速度。

重復利用的組件

當設計接口時,分解基本的設計元素(按鈕,表單,布局等)把他們變成可復用的組件。下次你建立UI的時候,你可以寫更少的代碼。這意味著縮短開發時間,減少bug,減少數據傳輸。

Prop 校檢

隨著你的app的成長,確保你的組件正確的使用也很重要。我們通過申明propTypes. React.PropTypes來做到這點。輸出一系列的驗證器可以確保你接收到的數據是有效的。當prop提供了一個無效的值時,警告會在JavaScript中拋出。注意,為了性能,propTypes只在開發模式下檢查。下面是不同的檢查器案例。

React.createClass({
  propTypes: {
    // You can declare that a prop is a specific JS primitive. By default, these
    // are all optional.
    optionalArray: React.PropTypes.array,
    optionalBool: React.PropTypes.bool,
    optionalFunc: React.PropTypes.func,
    optionalNumber: React.PropTypes.number,
    optionalObject: React.PropTypes.object,
    optionalString: React.PropTypes.string,
    optionalSymbol: React.PropTypes.symbol,

    // Anything that can be rendered: numbers, strings, elements or an array
    // (or fragment) containing these types.
    optionalNode: React.PropTypes.node,

    // A React element.
    optionalElement: React.PropTypes.element,

    // You can also declare that a prop is an instance of a class. This uses
    // JS's instanceof operator.
    optionalMessage: React.PropTypes.instanceOf(Message),

    // You can ensure that your prop is limited to specific values by treating
    // it as an enum.
    optionalEnum: React.PropTypes.oneOf(['News', 'Photos']),

    // An object that could be one of many types
    optionalUnion: React.PropTypes.oneOfType([
      React.PropTypes.string,
      React.PropTypes.number,
      React.PropTypes.instanceOf(Message)
    ]),

    // An array of a certain type
    optionalArrayOf: React.PropTypes.arrayOf(React.PropTypes.number),

    // An object with property values of a certain type
    optionalObjectOf: React.PropTypes.objectOf(React.PropTypes.number),

    // An object taking on a particular shape
    optionalObjectWithShape: React.PropTypes.shape({
      color: React.PropTypes.string,
      fontSize: React.PropTypes.number
    }),

    // You can chain any of the above with `isRequired` to make sure a warning
    // is shown if the prop isn't provided.
    requiredFunc: React.PropTypes.func.isRequired,

    // A value of any data type
    requiredAny: React.PropTypes.any.isRequired,

    // You can also specify a custom validator. It should return an Error
    // object if the validation fails. Don't `console.warn` or throw, as this
    // won't work inside `oneOfType`.
    customProp: function(props, propName, componentName) {
      if (!/matchme/.test(props[propName])) {
        return new Error(
          'Invalid prop `' + propName + '` supplied to' +
          ' `' + componentName + '`. Validation failed.'
        );
      }
    },

    // You can also supply a custom validator to `arrayOf` and `objectOf`.
    // It should return an Error object if the validation fails. The validator
    // will be called for each key in the array or object. The first two
    // arguments of the validator are the array or object itself, and the
    // current item's key.
    customArrayProp: React.PropTypes.arrayOf(function(propValue, key, componentName, location, propFullName) {
      if (!/matchme/.test(propValue[key])) {
        return new Error(
          'Invalid prop `' + propFullName + '` supplied to' +
          ' `' + componentName + '`. Validation failed.'
        );
      }
    })
  },
  /* ... */
});
});

獨生子 Single Child

通過React.PropTypes.element 你可以申明,自己只有一個孩子可以被傳入組件

var MyComponent = React.createClass({
  propTypes: {
    children: React.PropTypes.element.isRequired
  },

  render: function() {
    return (
      <div>
        {this.props.children} // This must be exactly one element or it will warn.
      </div>
    );
  }
});

默認的Prop值

React允許你定義初始值(保存在props中), 通過 getDefaultProps()設置的初始值會保證this.props.value有值(如果父層沒有申明),這可以讓你安全的使用props而不用寫重復的代碼來處理。

轉移Props:快捷鍵

一種常見的React組件類型就是繼承基本的HTML元素。你可能常常想復制HTML屬性,傳遞給你的組件里的HTML元素,為了少打點字,你可以使用JSX語法來實現。(譯者注:這段就說你要用JSX可以簡單點)

    class CheckLink extends React.Component {
          render() {
            // This takes any props passed to CheckLink and copies them to <a>
            return (
              <a {...this.props}>{'√ '}{this.props.children}</a>
            );
          }
        }

        ReactDOM.render(
          <CheckLink href="/checked.html">
            Click here!
          </CheckLink>,
          document.getElementById('example')
        );

無狀態Functions

如果一個組件不使用local state(怎么翻譯==就是你那個function范圍內的state)或者 lifecycle hooks,你可以把它定義成一個function而非class

    function Greeting(props) {
      return <h1>Hello, {props.name}</h1>;
    }

    ReactDOM.render(
      <Greeting name="Sebastian" />,
      document.getElementById('example')
    );

或者使用ES6語法:

    const Greeting = (props) => (
      <h1>Hello, {props.name}</h1>
    );

    ReactDOM.render(
      <Greeting name="Sebastian" />,
      document.getElementById('example')
    );

簡化后的組件API是為了成為基于props的擁有更純粹的functions的組件。這些組件不可以使用內部的state,不可以有引用的變量(backing instances),不可以有組件內生命周期的方法(component lifecycle methods)。他們只是使用輸入參數的單純方法,并且沒有使用任何引用變量。

但是,你還是可以聲明propTypes和defaultProps,設置他們為function的屬性,ES6就這么寫

function Greeting(props) {
  return (
    <h1>Hello, {props.name}</h1>
  );
}

Greeting.propTypes = {
  name: React.PropTypes.string
};

Greeting.defaultProps = {
  name: 'John Doe'
};

ReactDOM.render(
  <Greeting name="M?d?lina"/>,
  document.getElementById('example')
);

注意
因為沒有state的functions沒有引用變量,你不能給它附屬一個ref。通常情況下這不是個問題,因為沒有state的functions不需要提供必要的API。沒有必要的API,你拿著變量就啥也不能做。然而,如果一個用戶在無state的function中想要找到DOM節點,他們必須把function包裹在一個state組件中(譯者注:要有wrapper class),并且把ref附屬給那個包裹的組件上(wrapper class)。

理想情況下,你的許多組件都會是不需要state的functions。將來我們計劃優化這些組件,為了避免不必要的檢查和內存分配。

當你的組件中不需要local state或者lifecycle hooks,我們建議你把它申明為function。并且,我們推薦ES6 class語法來實現。

ES6 Classes 和 React.createClass()

通常你需要用普通的JavaScript class定義React組件

class Greeting extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

如果你不用ES6

var Greeting = React.createClass({
  render: function() {
    return <h1>Hello, {this.props.name}</h1>;
  }
});

聲明 Prop Types 和 默認的 Props

用functions和ES6 classes,propTypes和defaultProps被定義為組件屬性

class Greeting extends React.Component {
  // ...
}

Greeting.propTypes = {
  name: React.PropTypes.string
};

Greeting.defaultProps = {
  name: 'Mary'
};

用React.createClass(),你需要將propTypes定義為傳入對象的屬性,getDefaultProps()是一個方法

var Greeting = React.createClass({
  propTypes: {
    name: React.PropTypes.string
  },

  getDefaultProps: function() {
    return {
      name: 'Mary'
    };
  },

  // ...

});

設置初始state

In ES6 classes, you can define the initial state by assigning this.state
in the constructor:
ES6 classes中,你可以在constructor中通過給this.state賦值定義初始state

class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.state = {count: props.initialCount};
  }
  // ...
}

React.createClass()中,你需要單獨提供getInitialState方法,返回初始state

var Counter = React.createClass({
  getInitialState: function() {
    return {count: this.props.initialCount};
  },
  // ...
});

自動綁定

如果React組件是用ES6 classes聲明的,方法會遵從相同的ES6 classes語法。這意味著你不用給instance自動bind,你要在構造器中調用.bind(this)

class SayHello extends React.Component {
  constructor(props) {
    super(props);
    // This line is important!
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    alert('Hello!');
  }

  render() {
    // Because `this.handleClick` is bound, we can use it as an event handler.
    return (
      <button onClick={this.handleClick}>
        Say hello
      </button>
    );
  }
}

React.createClass() 就不需要bind 方法了

var SayHello = React.createClass({
  handleClick: function() {
    alert('Hello!');
  },

  render: function() {
    return (
      <button onClick={this.handleClick}>
        Say hello
      </button>
    );
  }
});

也就是說用ES6語法會在event handlers中多點代碼量,但是它的優勢是,會在大型程序中稍稍有更好的性能。如果你樂意多寫這幾個字,你可以啟用實驗性的屬性experimental Class Properties syntax proposal with Babel:

class SayHello extends React.Component {
  // WARNING: this syntax is experimental!
  // Using an arrow here binds the method:
  handleClick = () => {
    alert('Hello!');
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        Say hello
      </button>
    );
  }
}

請注意,上面的屬于實驗性語法,將來可能會改變,可能不會采用,保險起見,你可以使用箭頭函數,e.g. onClick={(e) => this.handleClick(e)}),或者React.createClass()

Mixins 混合類

注意
ES6不支持mixin。因此,如果你使用ES6語法,就不可以使用mixins。我們在使用mixins的代碼庫中發現大量的問題。所以不推薦使用。

有時候非常復雜的組件可能會分享共用的功能。也就是所謂的 cross-cutting concerns. React.createClass
允許你使用合法的mixins系統。一個常見的例子就是,一個組件想要在一段時間間隔后自我更新。使用setInterval()函數很簡單,但是很重要的一點事,當你不需要的時候要取消你的interval來節省空間。React提供 lifecycle methods可以讓你知道什么時候一個組件即將創建或銷毀。讓我們來用這些方法創建一個簡單的mixin,來提供簡單的setInterval()方法,可以在你的組件銷毀時候自動地清理。

var SetIntervalMixin = {
  componentWillMount: function() {
    this.intervals = [];
  },
  setInterval: function() {
    this.intervals.push(setInterval.apply(null, arguments));
  },
  componentWillUnmount: function() {
    this.intervals.forEach(clearInterval);
  }
};

var TickTock = React.createClass({
  mixins: [SetIntervalMixin], // Use the mixin
  getInitialState: function() {
    return {seconds: 0};
  },
  componentDidMount: function() {
    this.setInterval(this.tick, 1000); // Call a method on the mixin
  },
  tick: function() {
    this.setState({seconds: this.state.seconds + 1});
  },
  render: function() {
    return (
      <p>
        React has been running for {this.state.seconds} seconds.
      </p>
    );
  }
});

ReactDOM.render(
  <TickTock />,
  document.getElementById('example')
);

如果一個組件使用多個mixins,多個mixins定義了相同的lifecycle method(例如:多個mixins想要在銷毀時清理你的組件),所有的lifecycle methods都會被call。被call后,mixins按照了順序運行在里面定義的方法。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 本筆記基于React官方文檔,當前React版本號為15.4.0。 1. 安裝 1.1 嘗試 開始之前可以先去co...
    Awey閱讀 7,798評論 14 128
  • 深入JSX date:20170412筆記原文其實JSX是React.createElement(componen...
    gaoer1938閱讀 8,104評論 2 35
  • 以下內容是我在學習和研究React時,對React的特性、重點和注意事項的提取、精練和總結,可以做為React特性...
    科研者閱讀 8,291評論 2 21
  • 最近看了一本關于學習方法論的書,強調了記筆記和堅持的重要性。這幾天也剛好在學習React,所以我打算每天堅持一篇R...
    gaoer1938閱讀 1,719評論 0 5
  • “我厭惡的都是我賴以生存的, 我深愛的卻是我遙不可及的?!?/div>
    張萌萌Sophie閱讀 296評論 1 1