React 官方文檔翻譯二

It's a common pattern in React to wrap a component in an abstraction. The outer component exposes a simple property to do something that might have more complex implementation details.
You can use JSX spread attributes to merge the old props with additional values:

React常見的模式就是抽象地包裹一個組件。外部的組件會暴露一個簡單的屬性,用這個接口來完成更發的事情。你可以使用 JSX spread attributes合并props的值。

<Component {...this.props} more="values" />

(譯者注:就是說,要給組件傳入一個參數,就把props的參數傳進去,...是ES6的語法,合并了props中所有的變量。more是定義的一個屬性)

如果你不使用JSX,你可以用任何object helper例如ES6 Object.assign or Underscore _.extend

React.createElement(Component, Object.assign({}, this.props, { more: 'values' }));

剩下的教程都在解釋如何更好地實踐。它用了JSX和實驗的ECMAScript語法

Manual Transfer 手動傳遞參數

大多數情況下你應該明顯地把屬性傳遞過去,這確保你只能曝光一小部分的內部API,確保可以運行

function FancyCheckbox(props) {
  var fancyClass = props.checked ? 'FancyChecked' : 'FancyUnchecked';
  return (
    <div className={fancyClass} onClick={props.onClick}>
      {props.children}
    </div>
  );
}
ReactDOM.render(
  <FancyCheckbox checked={true} onClick={console.log.bind(console)}>
    Hello world!
  </FancyCheckbox>,
  document.getElementById('example')
);

但是name prop、title prop、onMouseOver的情況下呢?

在JSX中用 ... 傳遞參數

注意:
有時候,傳遞每個屬性是很單調脆弱的。這種情況下,你可以使用destructuring assignment來構建一個未知屬性的集合,在...后列出所有你需要知道的屬性。...other

var { checked, ...other } = props;

This ensures that you pass down all the props EXCEPT the ones you're consuming yourself.
這可以確保你傳遞了所有的props,除了那個你自己用掉的。

function FancyCheckbox(props) {
  var { checked, ...other } = props;
  var fancyClass = checked ? 'FancyChecked' : 'FancyUnchecked';
  // `other` contains { onClick: console.log } but not the checked property
  return (
    <div {...other} className={fancyClass} />
  );
}
ReactDOM.render(
  <FancyCheckbox checked={true} onClick={console.log.bind(console)}>
    Hello world!
  </FancyCheckbox>,
  document.getElementById('example')
);

注意:
這個上面的例子,checked prop也是一個有效的DOM屬性,如果你不用這樣用解構賦值,你可能會不可避免地把它也傳遞下去了。

當傳遞未知其他props時請一直使用destructuring解構賦值模式

function FancyCheckbox(props) {
  var fancyClass = props.checked ? 'FancyChecked' : 'FancyUnchecked';
  // ANTI-PATTERN: `checked` would be passed down to the inner component
  return (
    <div {...props} className={fancyClass} />
  );
}

使用和傳遞同個Prop

如果你的組件想要使用一個屬性但是同時還要傳遞它,你可以通過checked={checked}重新傳遞它。這是更好的傳遞完整props對象的方式,因為它便于重構并且起球--||(是的,lint)

function FancyCheckbox(props) {
  var { checked, title, ...other } = props;
  var fancyClass = checked ? 'FancyChecked' : 'FancyUnchecked';
  var fancyTitle = checked ? 'X ' + title : 'O ' + title;
  return (
    <label>
      <input {...other}
        checked={checked}
        className={fancyClass}
        type="checkbox"
      />
      {fancyTitle}
    </label>
  );
}

(譯者注:就是把checked放在屬性欄里傳遞)

注意:
順序很重要!把{...other}放在JSX props的前面,你可以保證你組件的consumer不重寫他們。這個例子中我們確保了input屬性是“checkbox”

Rest and Spread Properties ...

Rest properties可以讓你從一個object提取出剩下的屬性到一個新的object。它排除了其他在解構賦值模式的屬性,這是ECMAScript proposal
實驗性功能

var { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
x; // 1
y; // 2
z; // { a: 3, b: 4 }

注意:

為了用Babel 6轉換rest and spread properties,你需要安裝es2015
preset, the transform-object-rest-spread
plugin, 并且在babellrc文件中配置他們

用Underscore傳遞參數

如果你不用JSX,你可以使用一個library來實現這個功能。Underscore支持supports_.omit
來過濾出屬性和 _.extend復制屬性到一個新的object。

function FancyCheckbox(props) {
  var checked = props.checked;
  var other = _.omit(props, 'checked');
  var fancyClass = checked ? 'FancyChecked' : 'FancyUnchecked';
  return (
    React.DOM.div(_.extend({}, other, { className: fancyClass }))
  );
}

Forms

Form組件例如 <input>, <textarea>, <option>不同于原生組件,因為他們可以通過用戶的交互產生變化。這些組件提供了接口,讓你更簡單地管理form,處理好與用戶的交互。更多關于<form> events的信息 see Form Events.

交互的Props

Form組件支持一部分因為用戶交互受影響的props值

  • value, <input> <textarea>支持
  • checked, <input> type checkbox or radio
  • selected, <option>

在HTML中,<textarea>的值通過children來設置的。React中你應該通過value設置

Form組件允許監聽改變,設置一個callback在onChange prop。onChange prop會在瀏覽器上工作,來觸發用戶響應。當以下幾種情況:

  • value <input> <textarea>發生改變
  • checked <input>發生改變
  • selected <option> 發生改變

像所有的DOM事件一樣,所有原生組件都支持onChange prop,可以用來監聽冒泡事件。

注意:
<input> <textarea> 中,onChange事件被重寫了,用這個不要用DOM內置的那個event handler. oninput

Controlled Components 控制組件

controllledinput有value prop,設置<input>的默認值來自value prop的賦值

render() {
    return <input type="text" value="Hello!" />;
}

用戶輸入對渲染的組件毫無影響,因為React賦值了Hello!給value。如果要在response中更新用戶輸入,你可以使用onChange事件

class MyForm extends React.Component {
  constructor(props) {
    super(props);    
    this.state = {value: 'Hello!'};
    this.handleChange = this.handleChange.bind(this);
  }

  handleChange(event) {
    this.setState({value: event.target.value});
  }

  render() {
    return (
      <input
        type="text"
        value={this.state.value}
        onChange={this.handleChange}
      />
    );
  }
}

這個例子中,我們接受了用戶輸入的值并且更新了<input>的value prop。這個模式可以很簡單的響應或處理用戶的輸入,例如

handleChange(event) {
    this.setState({value: event.target.value.substr(0, 140)});
  }

這個例子讀取了用戶輸入并且只截取了前140個字符,一個Controlled組件不會一直維持它自己內部的狀態。這個組件單純地基于props進行渲染。

Checkboxes和Radio buttons的潛在問題

請注意,嘗試標準化checkbox和radio Inputs的handle change,React使用了點擊事件代替change事件。大部分情況下它表現正常,除了在change handler里調用 preventDefault的時候,preventDefault會阻止瀏覽器更新input值,即使checked已經被觸發。要解決這個問題,要不刪除掉call preventDefault,要不在setTimeout中啟用觸發checked事件

Uncontrolled組件

渲染input時候會給一個空值,任何用戶輸入會立即反映在渲染的元素上。如果你想要監聽value的變化,你可以像在controlled組件中一樣使用onChange事件。一個uncontrolled組件會維持它自己的內部狀態。

默認值

如果你想要初始化組件為一個非空值,你可以給prop設置一個默認值,例如:

render() {
    return <input type="text" defaultValue="Hello!" />;
  }

這個例子運行起來會像 Controlled Components 例子那樣,同樣地,<input type="checkbox">和<input type="radio">都支持defaultChecked,select支持defaultValue。

注意:
defaultValue和defaultChecked props值在初始化render中調用。如果你需要在之后的render中更新value,你將需要使用controlled component.

高級話題

Why Controlled Components?
Using form components such as <input>
in React presents a challenge that is absent when writing traditional form HTML. For example, in HTML:

為什么叫Controlled組件?

在React使用傳統的form HTMLform組件例如input會有不存在的問題。例如,HTML中:

<input type="text" name="title" value="Untitled" />

這個render了一個有初始化value值的input。當用戶更新input時候,節點值value屬性會變化。然而,node.getAttribute('value')仍會返回最初初始化的值。不像HTML,React組件必須代表當前時間下的view狀態,不只是初始化的狀態。例如React中:

render() {
    return <input type="text" name="title" value="Untitled" />;
  }

因為這個方法描述了當前時刻下的view,text input的值應該一直都是Untitled。

為什么Textarea Value?

HTML中,<textarea>的值通過children來設置的

  <!-- antipattern: DO NOT DO THIS! -->
  <textarea name="description">This is the description.</textarea>

HTML中,這方便用戶輸入多行文字,然而,因為React是JavaScript,我們沒有字符串限制,而且如果要換行可以用\n。總而言之,我們有value和defaultValue屬性,children的角色很難說明白。因此,在使用<textarea>時候你不應該使用children設置value

 <textarea name="description" value="This is a description." />

如果你非要用children,它就像defaultValue一樣。

為什么Select Value?

HTML中select選中的<option>一般是通過selected屬性聲明的。React中,為了讓組件更簡單操作,采用了下面的格式:

  <select value="B">
    <option value="A">Apple</option>
    <option value="B">Banana</option>
    <option value="C">Cranberry</option>
  </select>

創建一個uncontrolled組件,使用defaultValue。

注意:
你可以給value屬性傳遞一個數組,允許你在一個select中選中多個options:<select multiple={true} value={['B', 'C']}>

必要操作imperatively operation

如果你需要imperatively執行一個操作(就是你要在dom節點上操作),你必須獲取一個DOM節點的索引reference to the DOM node。例如,如果你想要imperatively提交一個表單,一個辦法就是給form元素附屬一個ref屬性,手動在form.submit()里面調用

與瀏覽器的工作

React provides powerful abstractions that free you from touching the DOM directly in most cases, but sometimes you simply need to access the underlying API, perhaps to work with a third-party library or existing code.

React提供了強大的分離功能,大多數情況下可以把你從直接接觸DOM中解放出來,但有些時候你可以直接訪問底層API, 可能調用第三方庫。

The Virtual DOM 虛擬的DOM

React非常快因為它從來都不直接和DOM對話。React保持了DOM在內存中快速的表現。render() 方法實際上返回的是一段對DOM的描述,React可以比較這段描述與內存上的表現,來計算出最快更新瀏覽器的方式。
另外,React完成了一套完整的合成的時間系統,如果忽略瀏覽器的怪異模式,所有的事件對象都確保遵從W3C聲明,每個事件都很穩定(everything bubbles consistently),在不同瀏覽器上都很搞笑。你甚至可以在舊版不支持的瀏覽器上使用HTML5 事件。
大多數時間你應該待在React的“假瀏覽器”世界,因為者更高效并且更簡單。但是,有時候你需要跳出來去訪問底層的API,可能需要與第三方庫合作,如jQuery。React提供了逃生窗口給你,來直接調用底層DOM API。

Refs and findDOMNode()

為了和瀏覽器交互,你將需要引用一個DOM節點。你可以給任何元素附屬一個ref屬性,它可以使你引用組件的backing instance。這很有用如果你需要調用組件的imperative functions, 或者想要訪問底層的DOM節點。了解更多refs,包括使用的方式,請看refs to components documentation.

組件周期

Components have three main parts of their lifecycle:
Mounting: A component is being inserted into the DOM.
Updating: A component is being re-rendered to determine if the DOM should be updated.
Unmounting: A component is being removed from the DOM.

React provides lifecycle methods that you can specify to hook into this process. We providewill methods, which are called right before something happens, and did methods which are called right after something happens.

組件有三個主要的生命周期:

  1. Mounting: 組件即將插入DOM.
  2. Updating: 組件即將重新渲染,判斷是否要更新DOM
  3. Unmounting:組件即將從DOM移除

Mounting

  • getInitialState(): 在組件mount之前被調用,有state的組件應該實現這個方法并且返回最初的state data
  • componentWillMount():在mouting之前被調用
  • componentDidMount(): 在mouting之后被調用。初始化需要的DOM節點放在這里

Updating

  • componentWillReceiveProps(object nextProps): 當一個已經掛載的組件接收到新的props的時候調用,這個方法應該被用來比較this.props和nextProps來實現state轉換(用this.setState()方法)

  • souldComponentUpdate(object nextProps, object nextState): boolean 當一個組件決定是否發生了應該去更新DOM的變化。實現這個方法的最佳辦法就是比較this.props和nextProps, this.state和nextState。 如果React跳過updating,則 return false

  • componentWillUpdate(object nextProps, object nextState) 在updating之前被調用,你不能在這里調用this.setState()

  • componentDidUpdate(object prevProps, object prevState) 在updating之后被調用

Unmounting

componentWillUnmount()在一個組件unmounted和destroyed之前。Cleanup應該放在這里

Mounted方法

Mounted復合的組件還支持下面的方法:
component.forceUpdate() 可以在任何掛載的組件上調用,當你知道更深層的組件狀態已經改變并且不使用this.setState()方法。

瀏覽器支持

React支持大多數瀏覽器,包括IE9及以上(不支持那些不支持ES5的瀏覽器)。

8.2 Refs to Components

建立了你的component之后,你可能發現想要“reach out”,調用組件變量從render() 返回的方法。大多數情況下,這是沒必要的因為React數據流確保了最新的props被送給render()輸出的每個child。然而,在一些情況下這還是有必要的或者有好處的,所以React提供了安全艙口,叫做refs。當你需要找到組件渲染的DOM標記,在非React項目中使用React組件、或者將你當前的代碼轉換成React,這些refs(references)特別有用。讓我們看看如何得到一個ref,以下是個完整例子。

從 ReactDOM.render返回的ref

不要和你在組件內定義的render()方法(它返回的是一個虛擬DOM)混淆。ReactDOM.render()將會返回一個組件的 backing instance(or null for stateless components).的reference索引。

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

記住,JSX不會返回一個組件變量!它只是一個React元素。一種輕量的表達方式說明React已經掛載的組件是什么模樣。

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

// Some code here...

var myComponentInstance = ReactDOM.render(myComponentElement, myContainer);
myComponentInstance.doSomething();

注意:
它只能再頂層使用。在組件內部,讓你的props和state來處理和子組件的數據傳遞,或者使用其他方法(string attribute or callbacks)獲取ref

The ref Callback Attribute

React支持一種你可以附屬在任何組件上的特殊的屬性。ref屬性可為一個callback function,callback會在組件 mount后立即執行。引用的組件會作為一個參數傳遞過去,callback function可以使用這個組件,或者儲存起來作為reference將來使用。
簡單地給render返回的任何東西添加ref值

render: function() {
    return (
      <TextInput
        ref={function(input) {
          if (input != null) {
            input.focus();
          }
        }} />
    );
},

當給一個DOM組件<div />附屬一個ref值時,你會得到返回的DOM節點,當附屬一個ref到復合的組件<TextInput />時,你會得到React class實例。之后,如果任何參數在class定義中暴露出來,你可以調用那個組件的方法。
當給一個DOM組件 <div />附屬一個ref值時,你會得到返回的DOM節點,當附屬一個ref到復合的組件<TextInput />時,你會得到React class實例。之后,如果任何參數在class定義中暴露出來,你可以調用那個組件的方法。

請注意當引用的組件unmounted卸載后,無論ref發生任何改變,調用舊的ref的值為null。這種情況是為了防止在儲存實例的時候內存泄漏(第二個例子就說明了這點)。還要注意到,像例子那樣在inline function表達式中使用當使用refs時,React在每次update時把每個function object看做是不同的,在調用組件實例之前,ref的值為null。

The ref String Attribute

注意:
盡管string refs沒有被棄用,被視為合法的,但是在不就的將來可能會被廢除。盡量使用Callback refs。

React還支持使用string(非callback)作為ref prop。

  1. 將ref屬性賦值給任何從render返回的東西。例如:

    <input ref="myInput" />

  2. 另一些代碼(通常是event handler ),通過this.refs訪問backing instance

    var input = this.refs.myInput;var inputValue = input.value;
    var inputRect = input.getBoundingClientRect();

A Complete Example
In order to get a reference to a React component, you can either use this
to get the current React component, or you can use a ref to get a reference to a component you own. They work like this:

一個完整的例子

為了得到React組件的例子,你可以使用這個來得到當前的React組件,或者你可以使用ref得到組件的引用reference。例如

var MyComponent = React.createClass({
  handleClick: function() {
    // Explicitly focus the text input using the raw DOM API.
    if (this.myTextInput !== null) {
      this.myTextInput.focus();
    }
  },
  render: function() {
    // The ref attribute is a callback that saves a reference to the
    // component to this.myTextInput when the component is mounted.
    return (
      <div>
        <input type="text" ref={(ref) => this.myTextInput = ref} />
        <input
          type="button"
          value="Focus the text input"
          onClick={this.handleClick}
        />
      </div>
    );
  }
});

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

這個例子中,我們得到了text input backing instance 的reference,還在button點擊的時候調用了focus()。對于符合的組件,reference指component class的實例,所以你能調用class定義的任何方法。如果你需要訪問組件的底層的DOM節點,你可以使用ReactDOM.findDOMNode 作為"escape hatch" 但是我們不推薦這么做,因為它會打破封裝,并且大多數情況下有種更清晰的方式在React model中規范的你的代碼結構。

Summary 總結

如果不方便通過streaming Reactive props和state傳遞信息到child instance,Refs是非常棒的方式。然后,他們不應該是你程序的數據流的抽象定位(go-to abstraction for flowing data through your application.)???我們默認使用React數據流,避免使用refs,因為refs沒繼承react數據流。

好處:

  • 你可以在組件class定義任何公共的方法(例如Typeahead的reset方法),通過refs調用這些公共的方法(例如this.refs.myTypeahead.reset()).大多數情況下,使用React內置的數據流比refs更清楚。

  • 執行DOM測量performing DOM measurements經常需要使用原生的組件(例如<input />),使用ref訪問底層的DOM節點。Refs是眾多可實現這一操作的可靠方法之一。

  • Refs會自我管理。如果這個child destroyed,它的ref也會自動銷毀。不用擔心此處的內存處理(除非你瘋了想要保留reference)

注意:

  • 不要在任何組件的render方法內部訪問refs,或者當然和組件的reder方法被調用的時候。

  • 如果你想要保留Google Closure Compiler advanced-mode crushing resilience,確保不要把ref當做字符串屬性來訪問,如果你的ref定義為ref="myRefString", 你必須使用this.refs['myRefString']。

  • 如果你沒有React寫過好幾個項目有,你會常常想使用refs來實現你app的效果。如果是這種情況,花一點時間來冷靜思考下在組件的繼承中,哪里應該使用state。經常,這回很清楚,state合適被放在更高level的繼承關系中。把state放在那里經常會減少你去使用ref來實現效果的欲望。數據流就會幫你實現目標。

  • Refs不可以被附屬到無state的function中,因為組件沒有backing instance。你可以把它包裹在沒有state的標準復合組件中,并且附屬ref給這個復合組件。

Tooling Integration 工具整合

我們已經盡可能讓React和環境無關。大家可以使用多種語言(JavaScript,TypeScript, ClojureScript)書寫React,在多種環境下(web, iOS,Android,NodeJS,Nashorn)使用React。有許多工具可以幫助你建立應用程序。下面的部分我們介紹一些常用的React工具。

Language Tooling

省略

Package Management

CDN-hosted React 使用CDN

我們提供了CDN服務 [on our download page (https://facebook.github.io/react/downloads.html)。這些內置的文件都是用UMD模塊格式。把他們扔進<script>即可開始書寫React和ReactDOM globals。在CommonJS和AMD環境中也生效。

Using React from npm

你可以在CommonJS模塊系統,例如browserify or webpack使用React。使用Use thereact
and react-dom
npm packages.

// main.js
var React = require('react');
var ReactDOM = require('react-dom');

ReactDOM.render(
  <h1>Hello, world!</h1>,
  document.getElementById('example')
);

用.babelrc文件配置 babel

 { "presets": ["react"] }

注意:
如果你使用ES2015,你需要使用 babel-preset-es2015

使用browserify安裝React DOM 簡歷你的bundle 捆綁:

$ npm install --save react react-dom babelify babel-preset-react
$ browserify -t [ babelify ] main.js -o bundle.js

webpack:

$ npm install --save react react-dom babel-preset-react babel-loader babel-core
$ webpack main.js bundle.js --module-bind 'js=babel-loader'

注意:
如果你使用ES2015,你需要使用 babel-preset-es2015

注意。默認地,React會處于開發者模式,它有點慢,但是利于開發。如果你想使用production模式,設置環境變量NODE_ENV為production (使用 envify or webpack's DefinePlugin)

new webpack.DefinePlugin({
  "process.env": {
    NODE_ENV: JSON.stringify("production")
  }
});

更新你的HTML

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Hello React!</title>
  </head>
  <body>
    <div id="example"></div>
    <script src="bundle.js"></script>
  </body>
</html>

Bower中使用React
Bower是一個為前端開發優化的軟件包管理器。如果一個package依賴多個packages,例如jQuery,Bower只會下載jQuery一次。這就是所謂的單個依賴關系圖,它會幫助你減少頁面負載。更多信息請查看http://bower.io/.

如果你想要使用bower,很簡單

bower install --save react

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Hello React!</title>
    <script src="bower_components/react/react.js"></script>
    <script src="bower_components/react/react-dom.js"></script>
    <script src="https://unpkg.com/babel-core@5.8.38/browser.min.js"></script>
  </head>
  <body>
    <div id="example"></div>
    <script type="text/babel">
      ReactDOM.render(
        <h1>Hello, world!</h1>,
        document.getElementById('example')
      );
    </script>
  </body>
</html>

Using master
We have instructions for building from master
in our GitHub repository.

Server-side Environments

Add-ons 擴展

The React add-ons are a collection of useful utility modules for building React apps. These should be considered experimental and tend to change more often than the core.

React擴展是關于一些創建React app功能模塊的集合。這些是實驗室性的功能,可能將來會變化。

The add-ons below are in the development (unminified) version of React only:
下面的擴展是在development version的React才能使用
TestUtils
, simple helpers for writing test cases.
Perf
, a performance profiling tool for finding optimization opportunities.

想要獲取擴展,從npm(npm安裝react-addons-pure-render-mixin)單個安裝他們。不支持除npm以外的工具。

Animation 動畫

React提供ReactTransitionGroup 擴展組件,作為動畫低級的API。ReactCSSTransitionGroup可以簡單地完成基礎的CSS動畫和轉場transitions。

高級的API: ReactCSSTransitionGroup

ReactCSSTransitionGroup是基于ReactTransitionGroup,在React組件進入或離開DOM的時候,幫助你很簡單的完成CSS transitions 和 animations。啟發于庫ng-animate .

開始

ReactCSSTransitionGroup 是ReactTransitions的接口。這是個簡單的元素,可以包裹所有的你感興趣的想要加上動畫的組件。這個例子是將列表中的項目淡入和淡出。

var ReactCSSTransitionGroup = require('react-addons-css-transition-group');

class TodoList extends React.Component {
  constructor(props) {
    super(props);
    this.state = {items: ['hello', 'world', 'click', 'me']};
    this.handleAdd = this.handleAdd.bind(this);
  }

  handleAdd() {
    var newItems = this.state.items.concat([
      prompt('Enter some text')
    ]);
    this.setState({items: newItems});
  }

  handleRemove(i) {
    var newItems = this.state.items.slice();
    newItems.splice(i, 1);
    this.setState({items: newItems});
  }

  render() {
    var items = this.state.items.map((item, i) => (
      <div key={item} onClick={() => this.handleRemove(i)}>
        {item}
      </div>
    ));

    return (
      <div>
        <button onClick={this.handleAdd}>Add Item</button>
        <ReactCSSTransitionGroup 
          transitionName="example" 
          transitionEnterTimeout={500} 
          transitionLeaveTimeout={300}>
          {items}
        </ReactCSSTransitionGroup>
      </div>
    );
  }
}

注意:
你必須給所有ReactCSSTransitionGroup的children提供the key
attribute
,即使在渲染一個單獨的item時,也要提供。這就是React如何決定哪個children進入, 離開,或者留下。

在這個組件中,當一個新的item加入到ReactCSSTransitionGroup時,它會生成example-enter CSS class, example-enter-active CSS class就被添加到下一個時刻中。這是根據transitionName prop生成的協定。你可以使用這些classes觸發CSS animation or transition。例如,試著添加這段CSS并且增加一個新的item

.example-enter {
  opacity: 0.01;
}

.example-enter.example-enter-active {
  opacity: 1;
  transition: opacity 500ms ease-in;
}

.example-leave {
  opacity: 1;
}

.example-leave.example-leave-active {
  opacity: 0.01;
  transition: opacity 300ms ease-in;
}

你會注意到animation durations需要在CSS和render 方法中同時聲明。這告訴React什么時候刪掉元素的animation classes。如果這個元素離開,從DOM中刪掉這個元素。

Animate Initial Mounting 讓最初的Mouting動起來

ReactCSSTransitionGroup提供了可選的prop transitionAppear,可以在最初組件mount的時候增加額外的transition階段。通常沒有在最初的mount時候transition phasis,因為transitionAppear的默認值是false。接下來的案例說明了誰向prop transitionAppear 傳遞了 true的值。

render() {
  return (
    <ReactCSSTransitionGroup 
      transitionName="example" 
      transitionAppear={true} 
      transitionAppearTimeout={500}>
      <h1>Fading at Initial Mount</h1>
    </ReactCSSTransitionGroup>
  );
}

During the initial mount ReactCSSTransitionGroup
will get the example-appear
CSS class and the example-appear-active
CSS class added in the next tick..

在最初的mount期間,ReactCSSTransitionGroup將會得到example-appear CSS class和加入下一時刻的example-appear-active CSS class

.example-appear {
  opacity: 0.01;
}

.example-appear.example-appear-active {
  opacity: 1;
  transition: opacity .5s ease-in;
}

在最初的mount,所有ReactCSSTransitionGroup的children會出現appear,而不是進入enter。之后, 所有的加入到ReactCSSTransitionGroup的children,開始enter,而不是appear。

注意:
prop transitionAppear是在版本0.13加入到ReactCSSTransitionGroup的。為了維持向后兼容,默認值為false

Custom Classes 自定義Classes

在transitions的每一步還可以使用自定義的class名字。不要傳遞string給transitionName,你可以傳遞一個包含enter leave class names Object, 或者包含enter, enter-active, leave-active, leave class names. 只要enter leave classes提供了,enter-active和leave active classes會自動加上-active后綴。這里有兩個例子

 ...
  <ReactCSSTransitionGroup
    transitionName={ {
      enter: 'enter',
      enterActive: 'enterActive',
      leave: 'leave',
      leaveActive: 'leaveActive',
      appear: 'appear',
      appearActive: 'appearActive'
    } }>
    {item}
  </ReactCSSTransitionGroup>

  <ReactCSSTransitionGroup
    transitionName={ {
      enter: 'enter',
      leave: 'leave',
      appear: 'appear'
    } }>
    {item2}
  </ReactCSSTransitionGroup>
  ...

Animation Group Must Be Mounted To Work

為了讓children也能有transitions, ReactCSSTransitionGroup必須已經掛載到DOM上,或者prop transitionAppear設置為true。
下面的例子無法工作,因為ReactCSSTransitionGroup正在mounted新item,而不是這個新item已經掛載上去了。和Getting Started 這個比較一下看看區別。

  render() {
    var items = this.state.items.map((item, i) => (
      <div key={item} onClick={() => this.handleRemove(i)}>
        <ReactCSSTransitionGroup transitionName="example">
          {item}
        </ReactCSSTransitionGroup>
      </div>
    ));

    return (
      <div>
        <button onClick={this.handleAdd}>Add Item</button>
        {items}
      </div>
    );
  }

(譯者注:嗯,我覺得就是要寫在return里。)

Animating One or Zero Items,讓1或0個item動起來

上面這個例子,我們在ReactCSSTransitionGroup渲染了一個列表的items。然而,ReactCSSTransitionGroup 的children可能是1個或0個,這就需要單個元素進入和離開的動畫。相似的,你可以animate新元素取代當前元素。例如,我們可以完成一個簡單的輪播:

var ReactCSSTransitionGroup = require('react-addons-css-transition-group');

function ImageCarousel(props) {
  return (
    <div>
      <ReactCSSTransitionGroup
        transitionName="carousel"
        transitionEnterTimeout={300}
        transitionLeaveTimeout={300}>
        <img src={props.imageSrc} key={props.imageSrc} />
      </ReactCSSTransitionGroup>
    </div>
  );
}

禁止動畫

你可以禁止進入或離開的動畫。例如,有時候你可能想要enter動畫不要leave動畫,但是ReactCSSTransitionGroup在離開DOM節點之前等待著一個動畫完成。你可以在 ReactCSSTransitionGroup設置addtransitionEnter={false} 或 transitionLeave={false} props來禁止動畫

注意:
當使用 ReactCSSTransitionGroup要注意,當一個transition結束、或者要展示邏輯復雜的動畫的時候。如果你想要更精準的控制,你可以使用低級的ReactTransitionGroup API,它更提供了你需要在自定義transition時使用的hooks

Low-level API: ReactTransitionGroup 低級API

ReactTransitionGroup是動畫的基礎。require('react-addons-transition-group')加上這句就可以使用了。當children從中添加或刪除時,特殊的生命周期hooks會被調用。

  • componentWillAppear(callback) 它和componentDidMount()調用的時間一樣,針對那些最初mount進TransitionGroup的組件。它會阻斷其他動畫的執行,直到callback函數被調用。只在 TransitionGroup最初的render中調用。

  • componentDidAppear() 在callback function之后調用,callback被傳遞到 componentWillAppear

  • componentWillEnter(callback) 與componentDidMount()調用的時間一樣,當組件唄加入到已經存在的TransitionGroup的時候。它會阻斷其他執行動畫直到callback被調用。在最初的TransitionGroup 的render中被調用。

  • componentDidEnter() 在callback function之后調用,被傳遞到componentWillEnter

  • componentWillLeave(callback) 當child從ReactTransitionGroup刪掉的時候調用。盡管child已經走了,ReactTransitionGroup還是在DOM中保留它直到callback被調用。

  • componentDidLeave() 當wiilLeave callback被調用,和componentWillUnmount同

Rendering a Different Component 渲染一個不同的組件

默認ReactTransitionGroup 作為一個span渲染。你可以通過提供component prop改變這點。例如,這是你如何render <ul>

<ReactTransitionGroup component="ul"> 
...
</ReactTransitionGroup>

任何附加的、用戶定義的屬性會成為已經渲染的組件的屬性。例如,這是你如何render 有CSS class的<ul>

<ReactTransitionGroup component="ul" className="animated-list"> 
...
</ReactTransitionGroup>

每個React可以render的DOM組件都可以使用。然而, 組件不需要是一個DOM組件。可以是任何你想要的組件,即使是你自己寫的。只要寫component={List}, 你的組件將會接收this.props.children

Rendering a Single Child 渲染單獨的child

人們經常使用ReactTransitionGroup來動畫一個單獨child的 mouting和unmounting,例如折疊面板。通常ReactTransitionGroup在一個span里包裹了所有的children(或者一個自定義的組件)。這是因為任何React組件都必須返回一個single root元素,并且ReactTransitionGroup沒有拋出異常的規則。然而,如果你只需要在ReactTransitionGroup里render一個single child, 你完全沒必要包裹在<span>里,或者任何DOM組件。要實現這個,創建一個自定義組件,render 第一個child并且直接傳遞它。

function FirstChild(props) {
  var childrenArray = React.Children.toArray(props.children);//為什么要toArray?
  return childrenArray[0] || null;
}

現在你可以在 <ReactTransitionGroup> props申明第一個child作為組件的prop,在result DOM中避免任何wrappers

<ReactTransitionGroup component={FirstChild}>
  {someCondition ? <MyComponent /> : null}
</ReactTransitionGroup>

這只在你animating 一個single child in and out時生效,例如折疊面板。當animating多個children或替換一個child為另一個(例如輪播)這種方法不生效。對于一個圖片輪播,當當前的圖片animating out,另一個圖片將會animate in。所以 <ReactTransitionGroup> 需要給他們一個通用的DOM parent。你不能避免wrapper多個children,但是你可以在component prop自定義這個wrapper

Context

React最大的優勢之一就是,很容易通過React組件去追蹤數據流。當你查看一個組件時,你可以很容易地看出哪個props被傳遞過來,這使得你的app易讀懂。

偶爾,你想要在么女額level上,手動通過組件tree,而不通過props傳遞數據。React的“context”特性就在這里。

注意:
Context是一個先進的實驗性特征。API可能會在將來改變。
大多數應用都不需要使用context。特別是如果你只是剛開始使用React,你可能不需要使用context。使用context會使你的代碼更難理解,因為它讓數據流變得不明顯。這類似于在你的應用中使用全局變量傳遞state。
如果你非要使用content,盡可能少用且不論你是否在簡歷app或者庫,盡量在一個小區域分離出你對context的使用,并且避免直接使用context API,這樣你在升級API的時候容易操作。

Passing info automatically through a tree 從一個tree自動傳遞信息

假設你有這樣的結構:

class Button extends React.Component {
  render() {
    return (
      <button style={{background: this.props.color}}>
        {this.props.children}
      </button>
    );
  }
}

class Message extends React.Component {
  render() {
    return (
      <div>
        {this.props.text} <Button color={this.props.color}>Delete</Button>
      </div>
    );
  }
}

class MessageList extends React.Component {
  render() {
    const color = "purple";
    const children = this.props.messages.map((message) =>
      <Message text={message.text} color={color} />
    );
    return <div>{children}</div>;
  }
}

在這個例子中,我們手動通過一個color prop來設置Button和Message組件的style。當你想要整個suvbtree都訪問信息(color),Theming 索引題目 是一個很好的例子。使用context我們可以自動在tree中傳遞this。

class Button extends React.Component {
  render() {
    return (
      <button style={{background: this.context.color}}>
        {this.props.children}
      </button>
    );
  }
}

Button.contextTypes = {
  color: React.PropTypes.string
};

class Message extends React.Component {
  render() {
    return (
      <div>
        {this.props.text} <Button>Delete</Button>
      </div>
    );
  }
}

class MessageList extends React.Component {
  getChildContext() {
    return {color: "purple"};
  }

  render() {
    const children = this.props.messages.map((message) =>
      <Message text={message.text} />
    );
    return <div>{children}</div>;
  }
}

MessageList.childContextTypes = {
  color: React.PropTypes.string
};

通過在MessageList(the context provider)添加childContextTypes和getChildContent,React自動向下傳遞了信息,任何在subtree中的組件都可以通過contextTypes訪問它。
如果contextTypes沒有定義,context將會為空object。

Parent-child coupling 相互依賴的父子

Context還能讓你建立API

<Menu>
  <MenuItem>aubergine</MenuItem>
  <MenuItem>butternut squash</MenuItem>
  <MenuItem>clementine</MenuItem>
</Menu>

通過在Menu組件向下傳遞相關信息,每個Menu Item 都可以和Menu組件交流。
在你用這個API建立組件之前,考慮下是否有更好的選擇。我們推薦你簡單地以數組形式傳遞items

<Menu items={['aubergine', 'butternut squash', 'clementine']} />

回憶下你還可以在props中傳遞整個React組件

Referencing context in lifecycle methods 在生命周期方法內引用context

如果contextTypes在一個組件內被定義,下面的lifecycle方法將會受到額外的參數,context object:

void componentWillReceiveProps(
  object nextProps, object nextContext
)

boolean shouldComponentUpdate(
  object nextProps, object nextState, object nextContext
)

void componentWillUpdate(
  object nextProps, object nextState, object nextContext
)

void componentDidUpdate(
  object prevProps, object prevState, object prevContext
)

Referencing context in stateless functional components 在沒有state的組件中引用context

無狀態的功能組件還可以reference context如果contextTypes被定義為function的屬性。下面的代碼展示了作為無狀態的Button組件。

const Button = ({children}, context) =>
  <button style={{background: context.color}}>
    {children}
  </button>;

Button.contextTypes = {color: React.PropTypes.string};

Updating context 更新context

getChildContext function在state或props改變的時候被調用。為了更新context的數據,用this.setState觸發本地的state更新。這會觸發一個新的context,children會接收到這些變化。

class MediaQuery extends React.Component {
  constructor(props) {
    super(props);
    this.state = {type:'desktop'};
  }

  getChildContext() {
    return {type: this.state.type};
  }

  componentDidMount() {
    const checkMediaQuery = () => {
      const type = window.matchMedia("(min-width: 1025px)").matches ? 'desktop' : 'mobile';
      if (type !== this.state.type) {
        this.setState({type});
      }
    };

    window.addEventListener('resize', checkMediaQuery);
    checkMediaQuery();
  }

  render() {
    return this.props.children;
  }
}

MediaQuery.childContextTypes = {
  type: React.PropTypes.string
};

When not to use context 什么時候不用context

就像在書寫清晰代碼時全局變量最好避免使用,在多數情況下你應該避免使用context。特別是,使用前再三考慮,來節省打印時間,用props代替。

context最好的使用案例就是暗中地傳遞已登錄的用戶、當前的語言、或主題信息。所有可能本來是真的全局變量,但是context讓你把范圍局限到單個React subtree。

不要使用context在組件間傳遞你的model data。在tree中明顯地傳遞你的數據更容易被理解。使用context會使你的組件更耦合,不利于重復利用,因為根據render的內容不同,表現可能大不一樣。

Known limitations 了解限制

如果一個組件變化提供了context value,如果在shouldComponentUpdate時,中間的parent return false,使用這個值的子節點不會更新。See issue#2517

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,818評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,185評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,656評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,647評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,446評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,951評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,041評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,189評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,718評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,602評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,800評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,316評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,045評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,419評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,671評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,420評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,755評論 2 371

推薦閱讀更多精彩內容

  • 深入JSX date:20170412筆記原文其實JSX是React.createElement(componen...
    gaoer1938閱讀 8,090評論 2 35
  • 自己最近的項目是基于react的,于是讀了一遍react的文檔,做了一些記錄(除了REFERENCE部分還沒開始讀...
    潘逸飛閱讀 3,433評論 1 10
  • 斜瞇著雙眼 窗外迷糊般虛幻 不會想以后方向 思想不該有重負 那是女主人的懷抱 依舊幾年溫暖 陪著時光慢慢變老 是我...
    湯和兵臨城下閱讀 126評論 0 0
  • 我們的生活中摻雜了太多的東西,在我們的生活中一直有著自己和那些現實脫軌的人,或許我們自己就是這樣的。 01 我們有...
    貓咪樂樂fc閱讀 660評論 0 2
  • 幾經輾轉,唐樂樂終于拿到了方格的聯系電話。 把號碼保存到手機,她把這11個數字念了一遍又一遍,也沒能平息激動的心情...
    亦真閱讀 347評論 1 0