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阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容

  • 深入JSX date:20170412笔记原文其实JSX是React.createElement(componen...
    gaoer1938阅读 8,048评论 2 35
  • 自己最近的项目是基于react的,于是读了一遍react的文档,做了一些记录(除了REFERENCE部分还没开始读...
    潘逸飞阅读 3,339评论 1 10
  • 斜眯着双眼 窗外迷糊般虚幻 不会想以后方向 思想不该有重负 那是女主人的怀抱 依旧几年温暖 陪着时光慢慢变老 是我...
    汤和兵临城下阅读 118评论 0 0
  • 我们的生活中掺杂了太多的东西,在我们的生活中一直有着自己和那些现实脱轨的人,或许我们自己就是这样的。 01 我们有...
    猫咪乐乐fc阅读 642评论 0 2
  • 几经辗转,唐乐乐终于拿到了方格的联系电话。 把号码保存到手机,她把这11个数字念了一遍又一遍,也没能平息激动的心情...
    亦真阅读 332评论 1 0