组件声明
React 组件声明与作用域绑定
目前组件中关于布局与数据绑定主要是基于
-
Compositional Components 组件的可随意组合性是其灵魂特性,笔者也有专门的章节来介绍组件的组合策略与最佳实践。通过组件的组合,你可以以较好地方式进行代码复用与分发。
-
Pure Components
React 中提倡的函数式组件不会有任何的副作用,并且下文中提及的Dumb Component 与Smart Component 的分隔与HOC 模式保证了组件的可测试性。 -
Basic LifeCycle
React 为其组件提供了一个基本的生命周期,这保证了我们对于组件更好地控制性,并且变相地也为我们提供了命名空间等分隔。
createElement
函数的调用,因此我们也可以直接以如下方式声明组件
class Hello extends React.Component {
render() {
return React.createElement('div', null, `Hello ${this.props.toWhat}`);
}
}
不过这种方式会增加整个代码的复杂度,也失去了
# ES6 Class
笔者推荐是全部使用
import React from "react";
/**
* Rendering <HelloMessage text='Hello Sarah' /> results in this HTML:
* <div>Hello Sarah</div>
*/
class HelloMessage extends Component {
render() {
return <div>{this.props.text}</div>;
}
}
## Component、
export default class RelatedSearch extends React.Component {
constructor(props) {
super(props);
this._handleClick = this._handleClick.bind(this);
}
_handleClick(suggestedUrl, event) {
event.preventDefault();
this.props.onClick(suggestedUrl);
}
render() {
return (
<section className="related-search-container">
<h1 className="related-search-title">Related Searches:</h1>
<Layout x-small={2} small={3} medium={4} padded={true}>
{this.props.relatedQueries.map((query, index) =>
<Link
className="related-search-link"
onClick={(event) =>
this._handleClick(query.searchQuery, event)}
key={index}>
{query.searchText}
</Link>
)}
</Layout>
</section>
);
}
}
而使用
const _handleClick(suggestedUrl, onClick, event) => {
event.preventDefault();
onClick(suggestedUrl);
};
const RelatedSearch = ({ relatedQueries, onClick }) =>
<section className="related-search-container">
<h1 className="related-search-title">Related Searches:</h1>
<Layout x-small={2} small={3} medium={4} padded={true}>
{relatedQueries.map((query, index) =>
<Link
className="related-search-link"
onClick={(event) =>
_handleClick(query.searchQuery, onClick, event)}
key={index}>
{query.searchText}
</Link>
)}
</Layout>
</section>
export default RelatedSearch;
当我们比较这两种不同的实现时,最简单的可观测的差异在于
单文件中的代码差异可能不太明显,不过这区区几行代码减少带来的意义却是巨大的。首先,函数式组件顾名思义即可知其不需要引入this
的困扰,this
的令人头大的部分都可以被忽略。这一点在我们进行事件绑定的时候很有作用
onClick={this.sayHi.bind(this)}>Say Hi</a>
onClick={sayHi}>Say Hi</a>
在无状态组件中我们不需要再显式地绑定this
关键字。除此之外,函数式组件只是简单的根据输入的
this
一直是令人困惑与头疼的东西,不同于其他有明确类模型定义的语言,this
会很飘忽不定,特别是在包含回调函数的逻辑中,往往错误就发生在你没有正确的绑定this
。而this
指针是指向当前组件的上下文,不过当我们写一些异步代码的时候,this
指针就有可能被重定向:
this.setState({ loading: true });
fetch('/').then(function loaded() {
this.setState({ loading: false });
});
上述代码执行时会报TypeError
的异常,这是因为在回调函数中this
指针被重定向之后不能再找到setState
函数。在传统的this
指针:
var component = this;
component.setState({ loading: true });
fetch('/').then(function loaded() {
component.setState({ loading: false });
});
这种方式简单易用,也适合于初学者理解,不过感觉是以粗劣的方式来解决,也不符合语言特性,下面我们会讨论其他几种解决方案。
React.createClass
来声明某个组件时,类中定义的成员方法的上下文会被自动地绑定当前组件对象,这样我们就可以将类成员方法作为回调函数传入异步方法:
React.createClass({
componentWillMount: function() {
this.setState({ loading: true });
fetch('/').then(this.loaded);
},
loaded: function loaded() {
this.setState({ loading: false });
}
});
这种方式相对于前者就优雅了很多,我们并不需要在组件中添加太多额外的代码。不过如果我们使用的
## Bind
bind
函数,可以来强制绑定该函数的this
指针。一旦某个函数的上下文被绑定之后,就不会受到调用者的影响。简单的调用方式如下:
this.setState({ loading: true });
fetch('/').then(function loaded() {
this.setState({ loading: false });
}.bind(this));
这种方式对于其他语言转换而来的开发者而言可能难于理解,另外这种直接在调用时绑定的方式也容易被粗心的开发者所忽略。::
双冒号操作符来表示绑定操作,即默认的将左值绑定到右侧的表达式。譬如我们如果自定义了如下的map
函数:
function map(f) {
var mapped = new Array(this.length);
for(var i = 0; i < this.length; i++) {
mapped[i] = f(this[i], i);
}
return mapped;
}
不同于
[1, 2, 3]::map(x => x * 2)
// [2, 4, 6]
换言之,传统的我们以call
或者封装的调用方式都可以转换为新的绑定表达式语法:
[].map.call(someNodeList, myFn);
// or
Array.from(someNodeList).map(myFn);
对于这种类似于数组的数据结构我们可以使用如下语法:
someNodeList::map(myFn);
而这种语法在
this.setState({ loading: true });
fetch('/').then(this::() => {
this.setState({ loading: false });
});
## Arrow Function
this
指针,即this
指针在创建时就被强制绑定而不会受到调用者的影响。
this.setState({ loading: true });
fetch('/').then(() => {
this.setState({ loading: false });
});
无论你嵌套定义了多少层函数,所有的箭头函数都会保存最初的上下文信息。不过这种方式的缺点在于我们无法再使用命名函数,这样在调试时候就不太方便了,匿名函数中抛出的异常都会被标识为anonymous function
。如果你是使用了this
的指向:
const loaded = () => {
this.setState({ loading: false });
};
// will be compiled to
var _this = this;
var loaded = function loaded() {
_this.setState({ loading: false });
};