React 官方文檔翻譯一

GUIDS

第一章 為什么使用React?

React

  • 一個(gè)提供了用戶接口的JavaScript庫(kù)。
  • 誕生于Facebook和Instagram項(xiàng)目中。
  • 許多人把它看做是MVC編程模式。
    我們編寫(xiě)React只為解決一件事:數(shù)據(jù)需要實(shí)時(shí)刷新的大型應(yīng)用程序

簡(jiǎn)單

當(dāng)相關(guān)數(shù)據(jù)發(fā)生改變時(shí),React會(huì)自動(dòng)更新所有的UI組件。你可以很簡(jiǎn)單地掌控不同狀態(tài)下的app

聲明式

當(dāng)數(shù)據(jù)改變時(shí),React就好像被點(diǎn)擊了更新按鈕一樣,知道如何更新需要改變的部分

建立復(fù)用組件

React都是為了建立可以重復(fù)利用的組件。實(shí)際上,你用React可以做的唯一一件事就是建立組件。因?yàn)榇a都是封裝起來(lái)的,組件讓代碼重復(fù)利用,方便測(cè)試,并且便于單獨(dú)考慮每個(gè)組件機(jī)制。

給它5分鐘

React挑戰(zhàn)了許多傳統(tǒng)的想法,你第一次看到這種想法可能覺(jué)得它很瘋狂Give it five minutes。 當(dāng)閱讀這篇指南時(shí),這些瘋狂的想法已經(jīng)在FB和Instagram還有其他網(wǎng)站上建立了上千個(gè)組件了。

學(xué)習(xí)更多

link.

第二章 展示數(shù)據(jù)

你能用UI做的最基本的事情就是顯示數(shù)據(jù)。React讓你很簡(jiǎn)單地展示數(shù)據(jù),并且讓用戶界面自動(dòng)地更新這些數(shù)據(jù)。

開(kāi)始吧~

先讓我們看個(gè)例子: 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"> //
         **寫(xiě)在這里 ** 
    </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
這個(gè)頁(yè)面,當(dāng)你在input輸入字符時(shí),react自動(dòng)更改為時(shí)間,即使你現(xiàn)在沒(méi)寫(xiě)任何代碼,React自動(dòng)給你完成了。它是怎么做到的呢?除非特殊需要,React不會(huì)直接操縱Dom本身,它先在內(nèi)部模擬的DOM身上執(zhí)行這些變化,為你計(jì)算出最有效的改變方式,再應(yīng)用到實(shí)際DOM中。

傳入組件的參數(shù)叫做props—— properties的簡(jiǎn)稱(chēng)。他們是通過(guò)JSX語(yǔ)法傳入的。你可以把他們當(dāng)做組件的常量(不可變的量),也就是說(shuō),不要更改this.props

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

一個(gè)限制:React組件只能渲染一個(gè)單獨(dú)的節(jié)點(diǎn)(譯者注:就是說(shuō)所有的東西要包在一個(gè)div里,或者別的tag里面)。如果你想返回多個(gè)節(jié)點(diǎn),他們必須報(bào)在一個(gè)單獨(dú)的根節(jié)點(diǎn)中。

JSX語(yǔ)法

我們強(qiáng)烈認(rèn)為,組件是分離模塊正確的方式,而不是模版或者邏輯地展示(display logic)。我們認(rèn)為標(biāo)記和代碼要密切結(jié)合在一起。另外,邏輯地展示經(jīng)常很復(fù)雜,并且用模版語(yǔ)言會(huì)讓代碼變得笨重。

我們發(fā)現(xiàn)最好的解決方法就是用Javascript直接生成HTML和組件樹(shù),這樣你就用真正的編程語(yǔ)言來(lái)建立UI。

為了更簡(jiǎn)單的說(shuō)明,我們?cè)黾恿艘粋€(gè)更簡(jiǎn)單的像HTML的語(yǔ)法來(lái)創(chuàng)建這些React樹(shù)節(jié)點(diǎn)。JSX可以讓你通過(guò)HTML語(yǔ)法創(chuàng)建JavaScript對(duì)象。在React中生成一個(gè)鏈接的JavaScript語(yǔ)法是

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

使用JSX的話,你就可以直接這樣寫(xiě)

    <a >Hello!</a>

我們發(fā)現(xiàn)這讓開(kāi)發(fā)React app開(kāi)發(fā)更簡(jiǎn)單,設(shè)計(jì)師更喜歡這種語(yǔ)法,但是每個(gè)人有自己的工作流,所以JSX不是React開(kāi)發(fā)所必須的
JSX很小,學(xué)習(xí)更多請(qǐng)看 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的命令行工具。

沒(méi)有JSX的React

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

   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'));

為了方便,你可以創(chuàng)建簡(jiǎn)寫(xiě)的工廠函數(shù)

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

對(duì)于常用的HTML標(biāo)簽,React已經(jīng)有內(nèi)置的factories

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

2.1 JSX in Depth

JSX is 是一個(gè)JavaScript語(yǔ)義延伸,看起來(lái)像XML。你可以用簡(jiǎn)單的JSX語(yǔ)義書(shū)寫(xiě)React

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

組件命名空間

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

// 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>
);

為了讓它更簡(jiǎn)便,命名空間*應(yīng)運(yùn)而生,他可以讓你使用組件的時(shí)候,可以用其他組件作為屬性。

你還需要聲明:

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

JSX 會(huì)在編譯的時(shí)候自動(dòng)handle這些。

JavaScript 表達(dá)式

Attribute 表達(dá)式

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

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

在JSX中沒(méi)有設(shè)置屬性的值則默認(rèn)為true, 設(shè)置了屬性后才會(huì)視為false。這個(gè)問(wèn)題時(shí)常出現(xiàn)在使用HTML中的disabled, required, checked, readOnly屬性的時(shí)候。

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

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

JavaScript表達(dá)式也可以用在children上

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

給你JSX添加注釋很簡(jiǎn)單,他們只是JS的語(yǔ)法。你要包在{ }里面

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} />;
多個(gè)Props是不好的

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

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

這是一種反模式,因?yàn)樗馕吨覀儾荒軒湍銠z查propTypes的正確性,這導(dǎo)致你的propTypes之后會(huì)報(bào)錯(cuò),模糊的堆疊追蹤???(a cryptic stack trace)。Props應(yīng)該是不變的,在某處改變props對(duì)象會(huì)造成無(wú)法預(yù)期的結(jié)果。所以此刻把它看成一個(gè)凍結(jié)的(不可更改的)對(duì)象。

延展屬性

現(xiàn)在你可以使用一個(gè)新的JSX特性,叫spread attributes

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

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

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

...是ES6的語(yǔ)法,我們支持這些語(yǔ)法來(lái)提供更簡(jiǎn)潔的JSX

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

JSX看起來(lái)像HTML但是有些重要的區(qū)別你需要知道

DOM的區(qū)別

為了實(shí)現(xiàn)跨平臺(tái)的統(tǒng)一,React完成了獨(dú)立于瀏覽器的事件和DOM系統(tǒng)。我們借機(jī)清掃了一些DOM未完善的地方。所有的DOM屬性(包括事件處理 event handler)都是駝峰命名,與JavaScript style一致。這里我們故意地打破了規(guī)則,因?yàn)樗乔昂竺艿摹?strong>然而,data-和aria-屬性 conform to the specs只是用小寫(xiě)。Style屬性接受一個(gè)駝峰法明明的JavaScript對(duì)象,而不是一個(gè)CSS字符串,這和JavaScript DOM style是一致的,并且能防止XSS安全漏洞。因?yàn)閏lass和for都是JavaScript的保留字, JSX元素內(nèi)置了DOM nodes DOM節(jié)點(diǎn),應(yīng)該使用className和htmlFor。自定義的元素可以使用class和for(eg.<my-tag class="foo" />。所有的事件對(duì)象遵照W3C規(guī)則,所有的events(包括submit)bubble correctly per the W3C spec. See Event System for more details.
The onChange事件就像你期待的那樣,無(wú)論是form field更改了,event被觸發(fā)而不是on blur. 我們有意地更改了這個(gè)瀏覽器的默認(rèn)表現(xiàn)因?yàn)閛nChange表現(xiàn)不當(dāng),React依賴這個(gè)事件來(lái)即使地響應(yīng)用戶輸入。更多查看 FormsForm input屬性,例如value和checked,還有textarea。 More here.

HTML Entities 實(shí)體

你可以在JSX中插入HTML實(shí)體

  <div>First · Second</div>

如果你想要?jiǎng)討B(tài)地顯示HTML內(nèi)容,你陷入double escaping問(wèn)題,因?yàn)闉榱朔乐筙SS漏洞攻擊, React默認(rèn)跳過(guò)要顯示的字符串(譯者注:翻譯的好爛,這里說(shuō)的是轉(zhuǎn)義字符的問(wèn)題,直接用就顯示成字符串了,看例子)

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

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

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

一個(gè)更安全的方法是找到 unicode對(duì)應(yīng)的數(shù)字 在JavaScript字符串里使用它

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

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

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

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

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

自定義HTML屬性

如果你要給HTML元素傳遞自定義的屬性,React默認(rèn)不會(huì)渲染它,你要加個(gè)前綴data-

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

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

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

網(wǎng)絡(luò)無(wú)障礙 屬性aria-* 會(huì)被好好的渲染

<div aria-hidden={true} />

第三章 交互性和動(dòng)態(tài)的UIs

你已經(jīng)學(xué)會(huì)了如何用React展示數(shù)據(jù) learned how to display data 現(xiàn)在看看如何交互UI組件

簡(jiǎn)單例子

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> 
        ); 
    }
}

事件驅(qū)動(dòng)和合成事件 Event Handling and Synthetic Events

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

系統(tǒng)內(nèi)部:自動(dòng)綁定和事件委托

系統(tǒng)內(nèi)部,React讓你的代碼易讀懂并且高性能

自動(dòng)綁定Autobinding當(dāng)創(chuàng)建JavaScript的callback函數(shù)時(shí),你常常需要bind方法來(lái)綁定this,好讓正確的this變量傳入。React中每個(gè)方法都會(huì)自動(dòng)綁定當(dāng)前的組件變量(除非你使用ES6語(yǔ)法)。React會(huì)緩存綁定方法,可以提高CPU和內(nèi)存的效力。還能讓你少打字。

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

組件和公平的(?Just)狀態(tài)機(jī)

React認(rèn)為UIs都是狀態(tài)機(jī)。UI有多鐘不同的狀態(tài),只需渲染這些不同的狀態(tài)就能很好地呈現(xiàn)你的UI。

React中,你只需要更新一個(gè)組件的狀態(tài),然后根據(jù)這個(gè)新?tīng)顟B(tài)render一個(gè)新的UI。React會(huì)高效率地自動(dòng)為你更新DOM

State 狀態(tài)機(jī)是如何工作的

一個(gè)常見(jiàn)的方法就是用setState(data, callback)通知React數(shù)據(jù)已經(jīng)改變了,這個(gè)方法會(huì)把新數(shù)據(jù)合并到當(dāng)前狀態(tài)this.state,再重新渲染組件,當(dāng)組件渲染完成,callback方法被調(diào)用。大部分時(shí)候你根本不用寫(xiě)callback方法,因?yàn)镽eact會(huì)好好為你更新UI的。

什么組件需要用到State呢?

你的大多數(shù)組件只是從props讀取數(shù)據(jù)再進(jìn)行渲染。然而,有時(shí)你需要獲取input的值,一個(gè)server的請(qǐng)求或者一段時(shí)間。這種情況下你要用state

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

一個(gè)常見(jiàn)的模式是,創(chuàng)建幾個(gè)不用state的組件來(lái)旋繞數(shù)據(jù),然后在這基礎(chǔ)上創(chuàng)建一個(gè)state組件,將state這個(gè)參數(shù)通過(guò)props傳遞給他們。State組件封裝了所有需要交互的邏輯,非state組件負(fù)責(zé)渲染數(shù)據(jù)。

在state什么該做?

State應(yīng)該包含數(shù)據(jù),組件的event handlers可以改變這些數(shù)據(jù),并更新UI。在真實(shí)的apps中,數(shù)據(jù)可能是很小的JSON串。當(dāng)創(chuàng)建state組件時(shí),考慮如何最小化地展示這個(gè)state,只在this.state里儲(chǔ)存必要的數(shù)據(jù),基于這個(gè)數(shù)據(jù)計(jì)算出其他需要的信息。你會(huì)發(fā)現(xiàn)這樣思考后書(shū)寫(xiě)出來(lái)的程序可以創(chuàng)造出最正確,因?yàn)榻ostate增加的冗余的計(jì)算值會(huì)導(dǎo)致在同步時(shí)存儲(chǔ)他們,而不是依賴React組件去計(jì)算。

在state什么不該做?

this.state應(yīng)該只包含需要呈現(xiàn)在UI上的極少的數(shù)據(jù),它不應(yīng)該包含:
計(jì)算出的數(shù)據(jù)。不要擔(dān)心根據(jù)state計(jì)算得到的值——如果你的計(jì)算都在render()中完成,更能保證你的UI是一致的。例如:如果你保存了一個(gè)list在state中,你想要render它的長(zhǎng)度的字符串形式,只要render()中使用this.state.listItems.length+'list items' 方法,而不是把這個(gè)結(jié)果儲(chǔ)存在state中去調(diào)用

React組件根據(jù)props和state在render()中創(chuàng)建

props中的重復(fù)數(shù)據(jù)如果可能的話,嘗試用props作為數(shù)據(jù)來(lái)源。一個(gè)有效的使用就是在state中儲(chǔ)存props,這樣你就能知道它之前的值,因?yàn)閜rops可能會(huì)根據(jù)父組件的渲染結(jié)果而改變。

多個(gè)組件

目前我們已經(jīng)知道了如何書(shū)寫(xiě)一個(gè)單獨(dú)的組件來(lái)戰(zhàn)士數(shù)據(jù),并且處理用戶輸入。接下來(lái)讓我們看看React最出色的特性:可組合性。

Motivation: Separation of Concerns

動(dòng)機(jī):分離關(guān)注點(diǎn)

通過(guò)創(chuàng)建模塊化的組件,可以復(fù)用有完善接口的組件,就像使用fuctions和classes一樣,你能從模塊化組件受益多多。特別是,通過(guò)創(chuàng)建新組建可以分離app的關(guān)注點(diǎn)。通過(guò)給你的程序創(chuàng)建個(gè)性化組化庫(kù),你能找到更適合更新UI的方式。

復(fù)合組件案例

讓我們創(chuàng)建一個(gè)簡(jiǎn)單的Avatar組件,用來(lái)展示Facebook頁(yè)面的圖片和名字。這個(gè)案例調(diào)用了Facebook Graph API

所有者

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

當(dāng)你創(chuàng)建了一個(gè)React組件示例時(shí),你可以在{ }包含額外的React組件或者JavaScript表達(dá)式

 <Parent><Child /></Parent>

父Parent可以通過(guò)this.porps.children讀取children子的內(nèi)容。this.props.children是一個(gè)特殊的數(shù)據(jù)結(jié)構(gòu),調(diào)用了 React.Children utilities 來(lái)操縱他們。

Child Reconciliation

Reconciliation是React用每個(gè)render更新DOM的過(guò)程。通常來(lái)說(shuō),子組件根據(jù)他們r(jià)ender的順序reconcil。例如,假設(shè)兩個(gè)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改變第一個(gè)child的內(nèi)容來(lái)更新DOM,并且destroy的最后一個(gè)child。React更具children的順序reconciles

擁有state的Children

對(duì)于大多數(shù)組件來(lái)說(shuō),這不是什么大問(wèn)題。然而,對(duì)于有state的組件來(lái)說(shuō),攜帶著this.state.保存的data,進(jìn)行render,就很有問(wèn)題。

大多數(shù)情況下,這些可以通過(guò)隱藏元素而不是destroy元素來(lái)規(guī)避。

// 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>

動(dòng)態(tài)的Children

這種情況就更復(fù)雜了。當(dāng)children被攪亂了(因?yàn)樗阉鹘Y(jié)果??)或者,如果新的組件被添加到list的前面(在stream中)。這些情況下,通過(guò)render的每個(gè)child的identity和state都必須保持,你可以給每個(gè)孩子分配個(gè)獨(dú)特的key

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

當(dāng)React reconciles有key的children時(shí),它會(huì)確保所有的key都儲(chǔ)存起來(lái)。key必須直接在組件的數(shù)組中提供,而不是在包含的HTML children組件的容器上。(譯者注:不是綁在li上,是綁在包含li的組件上)

數(shù)據(jù)流

React中,主人的數(shù)據(jù)流通過(guò)props從主人向奴隸組件傳遞。這是高效的單方向數(shù)據(jù)捆綁:主人在props上捆綁奴隸需要的數(shù)據(jù),主人根據(jù)props或者state進(jìn)行計(jì)算。因?yàn)檫@個(gè)過(guò)程遞歸地進(jìn)行,數(shù)據(jù)會(huì)自動(dòng)更新。

性能上需要注意的是

你可能認(rèn)為如果一個(gè)主任好多個(gè)奴隸節(jié)點(diǎn),要更新一次數(shù)據(jù)很奢侈。好消息就是JavaScript很高效,render()方法又很簡(jiǎn)單,素有大多數(shù)程序這個(gè)過(guò)程會(huì)非??臁A硗?,瓶頸總是發(fā)生在DOM的改變,而不是JS的遞歸。React會(huì)最優(yōu)化批處理和改變檢測(cè)。然而,有時(shí)你真的想對(duì)性能更精細(xì)地掌控。這時(shí)你需要重寫(xiě)override shouldComponentUpdate()。要 return false當(dāng)你想要React跳過(guò)處理subtree。See the React reference docs for more information.

注意:
如果 shouldComponentUpdate() returns false 當(dāng)數(shù)據(jù)發(fā)生改變時(shí),React不能同步更新UI。請(qǐng)確保你知道你在干什么,只用在當(dāng)你發(fā)現(xiàn)性能問(wèn)題的時(shí)候,不要低估JavaScript更新DOM的速度。

重復(fù)利用的組件

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

Prop 校檢

隨著你的app的成長(zhǎng),確保你的組件正確的使用也很重要。我們通過(guò)申明propTypes. React.PropTypes來(lái)做到這點(diǎn)。輸出一系列的驗(yàn)證器可以確保你接收到的數(shù)據(jù)是有效的。當(dāng)prop提供了一個(gè)無(wú)效的值時(shí),警告會(huì)在JavaScript中拋出。注意,為了性能,propTypes只在開(kāi)發(fā)模式下檢查。下面是不同的檢查器案例。

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.'
        );
      }
    })
  },
  /* ... */
});
});

獨(dú)生子 Single Child

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

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>
    );
  }
});

默認(rèn)的Prop值

React允許你定義初始值(保存在props中), 通過(guò) getDefaultProps()設(shè)置的初始值會(huì)保證this.props.value有值(如果父層沒(méi)有申明),這可以讓你安全的使用props而不用寫(xiě)重復(fù)的代碼來(lái)處理。

轉(zhuǎn)移Props:快捷鍵

一種常見(jiàn)的React組件類(lèi)型就是繼承基本的HTML元素。你可能常常想復(fù)制HTML屬性,傳遞給你的組件里的HTML元素,為了少打點(diǎn)字,你可以使用JSX語(yǔ)法來(lái)實(shí)現(xiàn)。(譯者注:這段就說(shuō)你要用JSX可以簡(jiǎn)單點(diǎn))

    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')
        );

無(wú)狀態(tài)Functions

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

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

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

或者使用ES6語(yǔ)法:

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

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

簡(jiǎn)化后的組件API是為了成為基于props的擁有更純粹的functions的組件。這些組件不可以使用內(nèi)部的state,不可以有引用的變量(backing instances),不可以有組件內(nèi)生命周期的方法(component lifecycle methods)。他們只是使用輸入?yún)?shù)的單純方法,并且沒(méi)有使用任何引用變量。

但是,你還是可以聲明propTypes和defaultProps,設(shè)置他們?yōu)閒unction的屬性,ES6就這么寫(xiě)

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')
);

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

理想情況下,你的許多組件都會(huì)是不需要state的functions。將來(lái)我們計(jì)劃優(yōu)化這些組件,為了避免不必要的檢查和內(nèi)存分配。

當(dāng)你的組件中不需要local state或者lifecycle hooks,我們建議你把它申明為function。并且,我們推薦ES6 class語(yǔ)法來(lái)實(shí)現(xiàn)。

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 和 默認(rèn)的 Props

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

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

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

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

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

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

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

  // ...

});

設(shè)置初始state

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

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

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

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

自動(dòng)綁定

如果React組件是用ES6 classes聲明的,方法會(huì)遵從相同的ES6 classes語(yǔ)法。這意味著你不用給instance自動(dòng)bind,你要在構(gòu)造器中調(diào)用.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>
    );
  }
});

也就是說(shuō)用ES6語(yǔ)法會(huì)在event handlers中多點(diǎn)代碼量,但是它的優(yōu)勢(shì)是,會(huì)在大型程序中稍稍有更好的性能。如果你樂(lè)意多寫(xiě)這幾個(gè)字,你可以啟用實(shí)驗(yàn)性的屬性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>
    );
  }
}

請(qǐng)注意,上面的屬于實(shí)驗(yàn)性語(yǔ)法,將來(lái)可能會(huì)改變,可能不會(huì)采用,保險(xiǎn)起見(jiàn),你可以使用箭頭函數(shù),e.g. onClick={(e) => this.handleClick(e)}),或者React.createClass()

Mixins 混合類(lèi)

注意
ES6不支持mixin。因此,如果你使用ES6語(yǔ)法,就不可以使用mixins。我們?cè)谑褂胢ixins的代碼庫(kù)中發(fā)現(xiàn)大量的問(wèn)題。所以不推薦使用。

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

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')
);

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 本筆記基于React官方文檔,當(dāng)前React版本號(hào)為15.4.0。 1. 安裝 1.1 嘗試 開(kāi)始之前可以先去co...
    Awey閱讀 7,911評(píng)論 14 128
  • 深入JSX date:20170412筆記原文其實(shí)JSX是React.createElement(componen...
    gaoer1938閱讀 8,174評(píng)論 2 35
  • 以下內(nèi)容是我在學(xué)習(xí)和研究React時(shí),對(duì)React的特性、重點(diǎn)和注意事項(xiàng)的提取、精練和總結(jié),可以做為React特性...
    科研者閱讀 8,396評(píng)論 2 21
  • 最近看了一本關(guān)于學(xué)習(xí)方法論的書(shū),強(qiáng)調(diào)了記筆記和堅(jiān)持的重要性。這幾天也剛好在學(xué)習(xí)React,所以我打算每天堅(jiān)持一篇R...
    gaoer1938閱讀 1,803評(píng)論 0 5
  • “我厭惡的都是我賴以生存的, 我深?lèi)?ài)的卻是我遙不可及的?!?/div>
    張萌萌Sophie閱讀 334評(píng)論 1 1

友情鏈接更多精彩內(nèi)容