設(shè)計接口的時候,把通用的設(shè)計元素(按鈕,表單框,布局組件等)拆成接口良好定義的可復(fù)用的組件。這樣,下次開發(fā)相同界面程序時就可以寫更少的代碼,也意義著更高的開發(fā)效率,更少的 Bug 和更少的程序體積。
隨著應(yīng)用不斷變大,保證組件被正確使用變得非常有用。為此我們引入 propTypes。React.PropTypes 提供很多驗證器 (validator) 來驗證傳入數(shù)據(jù)的有效性。當(dāng)向 props 傳入無效數(shù)據(jù)時,JavaScript 控制臺會拋出警告。注意為了性能考慮,只在開發(fā)環(huán)境驗證 propTypes。下面用例子來說明不同驗證器的區(qū)別:
React.createClass({
propTypes: {
// 可以聲明 prop 為指定的 JS 基本類型。默認
// 情況下,這些 prop 都是可傳可不傳的。
optionalArray: React.PropTypes.array,
optionalBool: React.PropTypes.bool,
optionalFunc: React.PropTypes.func,
optionalNumber: React.PropTypes.number,
optionalObject: React.PropTypes.object,
optionalString: React.PropTypes.string,
// 所有可以被渲染的對象:數(shù)字,
// 字符串,DOM 元素或包含這些類型的數(shù)組。
optionalNode: React.PropTypes.node,
// React 元素
optionalElement: React.PropTypes.element,
// 用 JS 的 instanceof 操作符聲明 prop 為類的實例。
optionalMessage: React.PropTypes.instanceOf(Message),
// 用 enum 來限制 prop 只接受指定的值。
optionalEnum: React.PropTypes.oneOf(['News', 'Photos']),
// 指定的多個對象類型中的一個
optionalUnion: React.PropTypes.oneOfType([
React.PropTypes.string,
React.PropTypes.number,
React.PropTypes.instanceOf(Message)
]),
// 指定類型組成的數(shù)組
optionalArrayOf: React.PropTypes.arrayOf(React.PropTypes.number),
// 指定類型的屬性構(gòu)成的對象
optionalObjectOf: React.PropTypes.objectOf(React.PropTypes.number),
// 特定形狀參數(shù)的對象
optionalObjectWithShape: React.PropTypes.shape({
color: React.PropTypes.string,
fontSize: React.PropTypes.number
}),
// 以后任意類型加上 `isRequired` 來使 prop 不可空。
requiredFunc: React.PropTypes.func.isRequired,
// 不可空的任意類型
requiredAny: React.PropTypes.any.isRequired,
// 自定義驗證器。如果驗證失敗需要返回一個 Error 對象。不要直接
// 使用 `console.warn` 或拋異常,因為這樣 `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'
};
}
/* ... */
});
當(dāng)父級沒有傳入 props 時,getDefaultProps() 可以保證 this.props.value 有默認值,注意 getDefaultProps 的結(jié)果會被 緩存。得益于此,你可以直接使用 props,而不必寫手動編寫一些重復(fù)或無意義的代碼。
有一些常用的 React 組件只是對 HTML 做簡單擴展。通常,你想少寫點代碼來把傳入組件的 props 復(fù)制到對應(yīng)的 HTML 元素上。這時 JSX 的 spread 語法會幫到你:
var CheckLink = React.createClass({
render: function() {
// 這樣會把 CheckList 所有的 props 復(fù)制到 <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} // 有且僅有一個元素,否則會拋異常。
</div>
);
}
});
組件是 React 里復(fù)用代碼最佳方式,但是有時一些復(fù)雜的組件間也需要共用一些功能。有時會被稱為 跨切面關(guān)注點。React 使用 mixins 來解決這類問題。
一個通用的場景是:一個組件需要定期更新。用 setInterval() 做很容易,但當(dāng)不需要它的時候取消定時器來節(jié)省內(nèi)存是非常重要的。React 提供 生命周期方法 來告知組件創(chuàng)建或銷毀的時間。下面來做一個簡單的 mixin,使用 setInterval() 并保證在組件銷毀時清理定時器。
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], // 引用 mixin
getInitialState: function() {
return {seconds: 0};
},
componentDidMount: function() {
this.setInterval(this.tick, 1000); // 調(diào)用 mixin 的方法
},
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')
);
關(guān)于 mixin 值得一提的優(yōu)點是,如果一個組件使用了多個 mixin,并用有多個 mixin 定義了同樣的生命周期方法(如:多個 mixin 都需要在組件銷毀時做資源清理操作),所有這些生命周期方法都保證會被執(zhí)行到。方法執(zhí)行順序是:首先按 mixin 引入順序執(zhí)行 mixin 里方法,最后執(zhí)行組件內(nèi)定義的方法。