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按照了顺序运行在里面定义的方法。