Skip to content
Zheng Ping edited this page Jun 7, 2017 · 13 revisions

React

概念

  • React通常用作MVC中的V。它是一个用于建立UI的JavaScript库,它常用于建立数据更迭频繁的大型应用。
  • React实质就是建立可复用的组件。它通过虚拟DOM提供了一个更好的编程模型。
  • React实现了一个灵活的数据流,它能够减少样板文件,并且比传统的数据绑定更简单。
  • React组件实现了一个render()方法,它接收输入数据并返回要显示的内容。render()通过this.props访问传给组件的输入数据。
  • React节点只会渲染一个根节点,如果你想返回多个节点,它们必须被封装到一个根节点中去。

JSX

  • JSX是一个JavaScript语法的扩展,它看上去类似于XML。它可用于被不同的预处理器把它翻译成标准的ECMAScript。React中JSX不是必须的,但是React推荐使用JSX,因为它用一个简明的语法来定义属性各异的结构树,JSX让你用HTML语法来创建JavaScript对象。客串的开发人员可能对它更熟悉,比如设计人员。
  • 在浏览器中使用JSX的最简单的方法是使用JSXTransformer,但是在生产环境中不要这么用。通过react-tools可以把JSX编译成JavaScript代码。
  • React可以渲染HTML标签(字符串)和React组件(类)。比如渲染一个HTML标签只需要在JSX中用小写的标签名:
var myDivElement = <div className="foo" />;
React.render(myDivElement, document.body);

要渲染一个React组件,只需创建一个大写字母开头的局部变量:

var MyComponent = React.createClass({/*...*/});
var myElement = <MyComponent someProperty={true} />;
React.render(myElement, document.body);

React的JSX使用大写和小写的转换来区分局部组件类和HTML标签。

注意:因为JSX也是JavaScript,像class和for之类的标识符不被鼓励用作XML的属性名。React DOM组件希望DOM属性名能用className和htmlFor来替代。

  • 代码转换, React JSX会把类似XML的语法转换成真实的JavaScript代码。XML元素、属性以及子节点标签将被转换成传给React.createElement的参数。比如:
var Nav;
var app = <Nav color="blue" />;

将被转换成:

var app = React.createElement(Nav, {color: "blue"});

注意上面为了使用<Nav />,变量Nav必须在作用域中。当displayName的值是undefined的时候,JSX会通过变量赋值来推断一个类的displayName。比如:

// Input (JSX)
var Nav = React.createClass({});
// Output (JS): 转换后的JavaScript代码
var Nav = React.createClass({displayName: "Nav"});

注意:JSX表达式总是会被转化成ReactElement。一种优化方法是在React.createElement中把ReactElement内联成对象文法来传递给转换代码。

  • (Namespaced Components)命名空间中的组件,如果你实现的组件中有很多孩子,那么命名空间中的组件就可以让你使用以其它组件作为属性的组件。比如:
var Form = MyFormComponent;
var App = (
  <Form>
    <Form.Row>
      <Form.Label />
      <Form.Input />
    </Form.Row>
  </Form>
);

这样,你只需要创建你的子组件”Sub-components”来作为主组件(main component)的属性:

var MyFormComponent = React.createClass({ … });

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

当编译你的代码的时候,JSX将会处理这些属性:

var App = (
  React.createElement(Form, null,
    React.createElement(Form.Row, null,
      React.createElement(Form.Label, null),
      React.createElement(Form.Input, null)
    )
  )
);

注意,该属性只在0.11及以后的版本可用

  • JavaScript表达式,要把JavaScript表达式作为属性值,需要把表达式用大括号对扩起来,而不是用双引号对。比如:
// JSX代码
var person = <Person name={window.isLoggedIn ? window.name : ‘’} />;
// 将会输出JS代码
var person = React.createElement(
  Person,
  {name: window.isLoggedIn ? window.name : ‘’}
);
  • 孩子表达式(Child Expressions),类似的,JavaScript表达式也可以被用来表示孩子:
// JSX代码
var content = <Container>{window.isLoggedIn ? <Nav /> : <Login />}</Container>;
// 将会输出JS代码
var content = React.createElement(
  Containter,
  null,
  window.isLoggedIn ? React.createElement(Nav) : React.createElement(Login)
);
  • 注释,在JSX中添加注释很容易,因为注释也只是JavaScript表达式。当你在一个标签的孩子节点位置处写注释时,你需要把它放在大括号对里面。比如:
var content = (
  <Nav>
    {/* child comment, put {} around */}
    <Person
      /* multi
         line
         comment */
      name={window.isLoggedIn ? window.name : ‘’} // end of line comment
    />
  </Nav>
};

JSX展开属性

  • 不稳定的属性是负面的东西,如果你不知道你想设置哪些属性,你可以以后把它们临时添加到对象:
var component = <Component />;
component.props.foo = x; // bad
component.props.bar = y; // also bad

这是反模式的,它意味着以后我们不能帮你检查出正确的的属性类型。也就是说你的属性类型错误会以一个隐晦的错误棧追踪而结束。在这个点上,属性应该被考虑成稳定的。在其它地方改变属性对象会带来意想不到的后果,所以属性对象在那个点上最好是一个被冻结的对象。

  • 展开属性,现在JSX提供了一个新的叫展开属性(spread attributes)的特性。
var props = {};
props.foo = x;
props.bar = y;
var component = <Component {…props} />;

你传入对象的属性会被拷贝到组件的属性中去。你可以这样使用多次,也可以把它和其他的属性组合到一起,指明属性的顺序很重要,后面的属性会覆盖前面的属性。比如:

var props = {foo: ‘default’};
var component = <Component {…prpos} foo={‘override’} />;
console.log(component.props.foo); // ‘override’
  • 古怪的 … 记号,操作符(或者叫展开操作符)已经在ES6中的数组里被支持了,在ES 7中甚至建议展开对象的其他属性。为了在JSX中提供一个清晰的语法,我们也采用了这些已支持的开发标准。

弄懂JSX

JSX有点像 HTML,但是它们还是有很多差别。

  • HTML实体,在JSX中,你可以往文法字符中插入HTML实体,比如:
<div>First &middot; Second</div>

如果你想在动态内容中显示一个HTML实体,你将会遇到双逃逸问题,因为React逃逸了所有你将要显示的字符串,这样是为了防止免受XSS攻击。比如:

// Bad: It displays “First &middot; Second”
<div>{‘First &middot; 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元素的混合数组,比如:

<div>{[‘First ‘, <span>&middot;</span>, ‘ Second’]}</div>

最后,不要忘了你总是可以插入原生的HTML:

<div dangerouslySetInnerHTML={{__html: ‘First $middot; Second’}} />
  • 自定义HTML属性,如果你把HTML规范中未定义的属性传给了HTML元素,React就不会渲染它们。如果你想使用自定义属性,你的属性名应该加上data-前缀。比如:
<div data-custom-attribute=“foo” />

aria-开始的Web可访问性属性可以被正常的渲染。比如:

<div aria-hidden={true} />

DOM的差异

React实现了一个独立于浏览器的事件和DOM系统,这是出于性能和兼容浏览器的考虑,我们借此机会来清扫一下浏览器的DOM实现中的一些粗糙边角。

  • 所有的DOM性质和属性(包括事件处理器)都是驼峰命名标准,这符合JavaScript风格的标准。我们故意破坏了这个规范,因为该规范没有一致性。比如,属性data-*aria-*就没有遵从这个规范,它们是全小写的。
  • style属性接收一个JavaScript对象而不是CSS字符串,该对象的属性以驼峰风格来命名。这同DOMstyle的JavaScript属性一致,他更高效,并且还能阻止XSS安全漏洞。
  • 所有的事件对象都遵从W3C规范,并且所有的事件(包括submit)都按照W3C正确的冒泡。
  • onChange事件也与你的期望一致,当一个表单项目发生改变时,这个事件就会发出,这和传统的blur不太一样,这里我们没有遵循已有的浏览器行为,是因为相对于它的名字,onChange有一点用词不当,React依靠该事件来对用户的输入进行实时的响应。

互动和动态UI

  • 事件处理和合成的事件,用React你可以简单地把你的事件处理器传递给一个驼峰命名的属性,这与你在常规的HTML中做的一样。React通过实现一个合成的事件系统来确保所有的事件都表现的同IE8及以上的浏览器相一致。如果你想把React用在触摸设备上,比如电话或平板上,你只需要简单地调用React.initializeTouchEvents(true);来激活对触摸事件的处理。
  • 揭开引擎盖:自动绑定和事件代理。**自动绑定:**当在JavaScript中创建回调函数时,你通常需要绑定一个方法到它的实例上去,这样this的值才会正确,而在React中每个方法被自动地绑定到它的组件实例上,React会缓存这个被绑定的方法,这样提升了CPU和内存效率,也减少了代码量。**事件代理:**React并没有把事件处理器绑定到节点上面,当React启动时,它在顶层用一个单独的事件监听器监听所有的事件。当一个组件被挂载或卸载时,事件处理器也会从内部的映射中简单的被添加或移除。当事件发生时,React通过这个映射就知道怎么分发它,如果这个映射中没有事件处理器,React的事件处理器就什么也不做。
  • 组件只是状态机,在React中,你只需简单的更新组件的状态,然后基于这个新的状态渲染一个新的UI,React会以最高效的方法来帮你更新DOM。
  • 状态是怎样工作的?把数据的变动通知给React的常用方法是调用setState(data, callback);,该方法把data融合进了this.state,并且重新渲染组件。

多样的组件(Multiple Components)

React最具吸引力的特性是:可组构性

  • 缘由:把不同的问题分离开来。通过构建模块化组件,该组件重用了其它接口定义良好的组件,你就拥有了许多已定义的函数。值得说明的是你可以通过构建新的组件把你的应用中的不同层面的东西分离开来。通过为你的应用构建一个自定义组件库,你可以用最适合于你的领域的方式来表述你的UI。下面是一个合成的例子,它通过构建一个简单的Avatar组件来用Facebook的Graph API来显示用户名和一个属性文件:
var Avatar = React.createClass({
  render: function() {
    return (
      <div>
        <ProfilePic username={this.props.username} />
        <ProfileLink username={this.props.username} />
      </div>
    );
  }
});

var ProfilePic = React.createClass({
  render: function() {
    return (
      <img src={‘http://graph.facebook.com/' + this.props.username + ‘/picture’} />
    );
  }
});

var ProfileLink = React.createClass({
  render: function() {
    return (
      <a href={‘http://www.facebook.com/' + this.props.username}>
        {this.props.username}
      </a>
    );
  }
});

React.render(
  <Avatar username=“pwh” />,
  document.getElementById(‘example’)
);
  • 所有权,在上面的例子中,Avatar的实例拥有ProfilePicProfileLink的实例。在React里面,属主是把其它组件设置为props的组件,一般来说,如果组件X在组件Yrender()方法中被创建,那么X就被Y所有。之前就说过,一个组件不能改变它的props——它们总是与它们的属主设置它们时的样子保持一致,这个关键特性保证了UI的一致性。 区分属主拥有关系父子关系非常重要,属主拥有关系是React特有的,然而父子关系在DOM模型中已经很常见了。在上面的例子中,Avatar拥有divProfilePicProfileLink实例,而divProfilePicProfileLink实例的父亲,但不是它们的属主。

  • 孩子,当你创建一个React实例的时候,你可以把其它的React组件或JavaScript表达式放在标签里面,比如:<Parent><Child /></Parent>Parent通过访问this.props.children属性来读取它的孩子this.props.children是一个难懂的数据结构,使用React.Children工具可以操纵它们。

  • 孩子调和(Child Reconciliation),调和(Reconciliation)是React用每一个渲染过程更新DOM的过程。一般来说,孩子通过它们被渲染的顺序来被调和。假设两个渲染过程分别产生了下面的标签:

// 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通过改变第一个孩子的文本内容并销毁最后一个孩子来调和DOM。React的调和依赖于孩子的顺序。

  • 状态化孩子(Stageful Children),对于大多数组件,这没什么大不了的。但是,对于在渲染过程中维护着this.state中的数据的状态化组件而言,这里可能就有很多问题。在大多数情况下,可以通过隐藏元素而不是销毁来回避这个问题,比如:
// 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>
  • 动态化的孩子,当孩子的顺序被打乱或新的组件被添加到列表的表头时,这种情况下问题就更加复杂。此时,每个孩子的特征和状态必须在渲染过程中维护好,你可以通过赋给孩子一个key来唯一标识它,比如:
render: function() {
  var results = this.props.results;
  return (
    <ol>
      {results.map(function(result) {
        return <li key={result.id}>{result.text}</li>;
      })}
    </ol>
  );
}

当React调和被标注了key的孩子时,它会确保任意有key的孩子将被重新排序或者是被销毁。key应该永远被在数组中的组件直接支持,而不是在数组中的每个组件的HTML孩子容器。比如:

// WRONG!
var ListItemWrapper = React.createClass({
  render: function() {
    return <li key={this.props.data.id}>{this.props.data.text}</li>;
  }
});
var MyComponent = React.createClass({
  render: function() {
    return (
      <ul>
        {this.props.results.map(function(result) {
          return <ListItemWrapper data={result}/>;
        })}
      </ul>
    );
  }
});

// Correct :)
var ListItemWrapper = React.createClass({
  render: function() {
    return <li>{this.props.data.text}</li>;
  }
});
var MyComponent = React.createClass({
  render: function() {
    return (
      <ul>
        {this.props.results.map(function(result) {
          return <ListItemWrapper key={result.id} data={result}/>;
        })}
      </ul>
    );
  }
});

你也可以通过传入一个ReactFragment对象来对孩子加key。可以参考Keyed Fragments。

  • 数据流,在React中,数据流通过上面讨论过的props从属主传给属主所拥有的组件。这是一种高效的单程数据绑定:属主绑定它们拥有的组件的props为某个值,该值是基于属主自己的propsstate计算而来。因为这个过程是递归进行的,数据的改变也会自动地在它们被使用的地方反映出来。

  • 关于性能的提示,你可能会认为如果一个属主拥有大量的节点,React改变数据时开销会很大,你不必过多担心,因为JavaScript很快,而且render()方法也非常简单,所有在大多数应用中这会相当快。另外,瓶颈通常源于DOM的变化,而非JavaScript的执行,并且React会通过批处理和对更改进行探测来帮你优化。有时,你想自己控制你的组件动作,这时,你可以覆盖shouldComponentUpdate()让它返回false,这样React就不会处理它里面的subtree了。注意,如果shouldComponentUpdate()返回false,那么当数据改变时,React就不能保持你的UI同步。当你使用它时请确保你知道你在做什么,你应该在遇到明显的问题时才使用它。千万不要低估JavaScript相对于DOM的速度。

可重用的组件

当设计接口的时候,把常规的设计元素(按钮,表单域、布局组件等)放到接口定义良好的可重用的组件中,下次当你再构建UI的时候,你就可以少写代码,并开发得更快,同时bug更少。

  • 属性验证。随着你的应用的增长,有必要保证你的组件被正确的使用,你可以通过使用propTypes来做到这点。React.propTypes会导出一个用来验证你接收的数据是否合法的验证符的范围。当一个属性被传给了一个非法的值时,在JavaScript控制台中将会显示一个警告信息。注意,出于性能考虑,propTypes只在开发模式下才会被检查。下面是一个验证符的例子:
React.createClass({
  propTypes: {
    //you can declare that a prop is a specific JS primitive. By default, these are 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,
    
    //Anything that can be rendered: numbers, strings, elements or an array 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 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 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.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(‘validation failed!’);
      }
    },
    /* . . . */
  }
});
  • 默认属性值,React用一种声明式的方法让你可以定义props的默认值:
var ComponentWithDefaultProps = React.createClass({
  getDefaultProps: function() {
    return {
      value: ‘default value’
    };
  }
});

方法getDefaultProps()的结果会被缓存,如果父级组件没有指定this.props.value的值,这个被缓存的结果就会用作它的默认值。这使得你能够安全的使用你的props而不需要写重复的碎片化的代码。

  • 转移props的捷径,一种常见的React组件是对一个基础的HTML进行简单的扩展。为了节省打字的时间,你通常想把传给你的组件的所有的HTML属性拷贝到底层的HTML元素。你可以用JSX的展开语法来实现它:
var CheckLink = React.createClass({
  render: function() {
    //This takes any props passed to CheckLink and copies them to <a>
    return <a {…this.props}>{'√ '}{this.props.children}</a>;
  }
});


React.render({
  <CheckLink href=“/checked.html”>
    Click here!
  </CheckLink>,
  document.getElementById(‘example’)
});
  • 单个孩子,通过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 throw.
      </div>
    );
  }
});
  • 合成(mixins),组件是React中重用代码的最佳方案,但是有些时候,不同的组件间可能需要共享一部分功能代码。这个问题有时被称作“横切关注点(cross-cutting concerns)”,React通过mixins来解决这个问题。

一个常见的用例是一个组件想定期地更新自己,你可能马上会想到setInterval(),当你不再需要这个组件的时候取消定时器很重要。React提供了生命期方法(lifecycle methods),这样你就能够知道组件何时被创建和销毁。下面的例子用这些方法创建了一个简单的合成,它提供的定时器可以在组件被销毁的时候自动被清理。

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

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

React.render(
  <TickTock />,
  document.getElementById(‘example’)
);

合成(mixins)的一个动人的特性是如果一个组件用到了多个合成,并且某些合成定义了同样的生命期方法(比如某些合成会在组件被销毁的时候做一些清理工作),所有的生命期方法都能确保被调用到。合成中定义的方法会按照它们被列出在组件的mixins属性中的顺序被调用,

  • ES6风格的类

你可以把你的React类定义成一个纯JavaScript类,比如你可以使用ES6的class语法:

class HelloMessage extends React.Component {
  render() {
    return <div>Hello {this.props.name}</div>;
  }
}
React.render(<HelloMessage name=“Setastian” />, mountNode);

上面的API类似于React.createClass,但是getInitialState例外,不同于在React.createClass中提供一个getIntialState方法,你只需要在构造器里设置你自己的state。另一个不同是propTypesdefaultProps在构造器里被定义为属性,而不是在类体(class body)里面定义。比如:

export class Counter extends React.component {
  constructor(props) {
    super(props);
    this.state = {count: props.initialCount};
  }
  tick() {
    this.setState({count: this.state.count + 1});
  }
  render() {
    return (
      <div onClick={this.tick.bind(this)}>
        Clicks: {this.state.count}
      </div>
    );
  }
}
Counter.propTypes = {initialCount: React.propTypes.number};
Counter.defaultProps = {initialCount: 0};
  • 没有自动绑定

对于常规ES6中的方法,不会自动地把this绑定到实例,因而你必须显式地使用.bind(this)或者是箭头函数(arrow function)。

  • 没有合成

不幸的是ES6中不支持合成(mixins),将来的React版本无需合成也能支持这个用途。

转移属性

在React中通过抽象来封装一个组件是很常见的模式。在外层的组件暴露一个简单的属性来做某事,但是在实现的细节上面它可能比较复杂。你可以用JSX的展开属性(spread attributes)来把老属性和新的属性值合并起来,比如:

return <Component {…this.props} more=“values” />;

如果你不用JSX,你可以用任意的帮助对象,比如ES6的Object.assign或Underscore的_.extend

return Component(Object.assign({}, this.props, { more: ‘values’ }));

下面的教程展示了最佳实践,它们用到了JSX和试验性的ES7语法。

  • 手动转移

多数时候,你应该准确地把属性向下传递,这样你就只需暴露一个内部API的子集。比如:

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

但是如果有name属性、title属性或onMouseover属性,又该怎么办呢?

  • 用JSX的...转移

有时候把每个属性都列出来真的是有点繁琐,此时你可以使用解构赋值(destructuring assignment)来把其它属性直接提取到属性集中去。比如通过...other列出你想要的其它所有属性:

var {checked, ...others} = this.props;

这样你就可以把所有的你不想使用动属性继续向下传递。比如:

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

注音在上例中,checked属性也是一个合法的DOM属性,如果你不使用解构你有可能会因为粗心而把它传进去。上面的例子中需要用到harmony参数,因为它用到了ES7中点实验性语法。如果是在浏览器中使用JSX,你的标签应该这样写<script type="text/jsx;harmony=true">

不使用解构提取有用的属性而直接使用也是不好的模式,比如:

var FancyCheckbox = React.createClass({
  render: function() {
    var fancyClass = this.props.checked ? 'FancyChecked' : 'FancyUnchecked';
    // Anti-Pattern, `checked` would be passed down to the inner component
    return (
      <div {...this.props} className={fancyClass} />
    );
  }
});
  • 使用和传递相同的属性

如果你的组件既要用一个属性同时还要把它向下传递,你可以明确地重新传递它。这样做比传递整个this.props更好,因为它能更易于重构和捆绑(lint).比如:

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

上面的例子要注意属性的书写顺序,把...other写在前面你就可以确保它不会覆盖你指明的属性,比如上例的type属性。

组件的生命周期

组件的生命周期分成三个状态:

  • Mounting:已插入真实DOM
  • Updating:正在被重新渲染
  • Unmounting:已移出真实DOM

React为每个状态提供了两个处理函数,will函数在进入状态之前调用,did函数在进入状态之后调用,三种状态共计五种处理函数。

  • componentWillMount()
  • componentDidMount()
  • componentWillUpdate()
  • componentDidUpdate()
  • componetWillUnmount()

此外,React还提供两种特殊状态的处理函数:

  • componentWillReceiveProps(object nextProps): 已加载组件收到新的参数时调用
  • shouldComponentUpdate(object nextProps, object nextState): 组件判断是否重新渲染时调用

props VS state

组件的主要职责是把数据转换成HTML. 可以把props + state看成组件的render()函数的的输入数据. 属性和状态的值一旦确定, 组件输出的HTML也就确定了; 属性或状态的改变都会造成组件重新渲染.

一般把属性作为组件的配置选项, 而把那些可能被外界改变的数据用作状态. 组件一旦实例化后属性就不可变了, 组件中的属性是一种父级向子级传递数据的方式; 状态一般都有初始值, 状态一般是小型能被JSON序列化的数据.