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 href="https://facebook.github.io/react/">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()方法又很简单,素有大多数程序这个过程会非常快。另外,瓶颈总是发生在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阅读 6,194评论 14 128
  • 深入JSX date:20170412笔记原文其实JSX是React.createElement(componen...
    gaoer1938阅读 5,589评论 2 33
  • 以下内容是我在学习和研究React时,对React的特性、重点和注意事项的提取、精练和总结,可以做为React特性...
    科研者阅读 6,058评论 2 21
  • 最近看了一本关于学习方法论的书,强调了记笔记和坚持的重要性。这几天也刚好在学习React,所以我打算每天坚持一篇R...
    gaoer1938阅读 973评论 0 5
  • “我厌恶的都是我赖以生存的, 我深爱的却是我遥不可及的。”
    张萌萌Sophie阅读 48评论 1 1