深入理解React高階組件
1.在React中higher-order component (HOC)是一種重用組件邏輯的高級技術(shù)。HOC不是React API中的一部分。HOC是一個(gè)函數(shù),該函數(shù)接收一個(gè)組件并且返回一個(gè)新組件。在React中,組件是代碼復(fù)用的基本單位。
2.為了解釋HOCs,舉下面兩個(gè)例子
CommentList組件會渲染出一個(gè)comments列表,列表中的數(shù)據(jù)來自于外部。
class CommentList extends React.Component {
constructor() {
super();
this.handleChange = this.handleChange.bind(this);
this.state = {
// "DataSource" is some global data source
comments: DataSource.getComments()
};
}
componentDidMount() {
// Subscribe to changes
DataSource.addChangeListener(this.handleChange);
}
componentWillUnmount() {
// Clean up listener
DataSource.removeChangeListener(this.handleChange);
}
handleChange() {
// Update component state whenever the data source changes
this.setState({
comments: DataSource.getComments()
});
}
render() {
return (
<div>
{this.state.comments.map((comment) => (
<Comment comment={comment} key={comment.id} />
))}
</div>
);
}
}
接下來是BlogPost組件,這個(gè)組件用于展示一篇博客信息
class BlogPost extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.state = {
blogPost: DataSource.getBlogPost(props.id)
};
}
componentDidMount() {
DataSource.addChangeListener(this.handleChange);
}
componentWillUnmount() {
DataSource.removeChangeListener(this.handleChange);
}
handleChange() {
this.setState({
blogPost: DataSource.getBlogPost(this.props.id)
});
}
render() {
return <TextBlock text={this.state.blogPost} />;
}
}
這兩個(gè)組件是不一樣的,它們調(diào)用了DataSource的不同方法,并且它們的輸出也不一樣,但是它們中的大部分實(shí)現(xiàn)是一樣的:
1.裝載完成后,給DataSource添加了一個(gè)change listener
2.當(dāng)數(shù)據(jù)源發(fā)生變化后,在監(jiān)聽器內(nèi)部調(diào)用setState
3.卸載之后,移除change listener
可以想象在大型應(yīng)用中,相同模式的訪問DataSource和調(diào)用setState會一次又一次的發(fā)生。我們希望抽象這個(gè)過程,從而讓我們只在一個(gè)地方定義這個(gè)邏輯,然后在多個(gè)組件中共享。
接下來我們寫一個(gè)創(chuàng)建組件的函數(shù),這個(gè)函數(shù)接受兩個(gè)參數(shù),其中一個(gè)參數(shù)是組件,另一個(gè)參數(shù)是函數(shù)。下面調(diào)用withSubscription函數(shù)
const CommentListWithSubscription = withSubscription( CommentList, (DataSource) => DataSource.getComments() ); const BlogPostWithSubscription = withSubscription( BlogPost, (DataSource, props) => DataSource.getBlogPost(props.id) );
調(diào)用withSubscription傳的第一個(gè)參數(shù)是wrapped 組件,第二個(gè)參數(shù)是一個(gè)函數(shù),該函數(shù)用于檢索數(shù)據(jù)。
當(dāng)CommentListWithSubscription和BlogPostWithSubscription被渲染,CommentList和BlogPost會接受一個(gè)叫做data的prop,data中保存了當(dāng)前從DataSource中檢索出的數(shù)據(jù)。withSubscription代碼如下:
// This function takes a component...
function withSubscription(WrappedComponent, selectData) {
// ...and returns another component...
return class extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.state = {
data: selectData(DataSource, props)
};
}
componentDidMount() {
// ... that takes care of the subscription...
DataSource.addChangeListener(this.handleChange);
}
componentWillUnmount() {
DataSource.removeChangeListener(this.handleChange);
}
handleChange() {
this.setState({
data: selectData(DataSource, this.props)
});
}
render() {
// ... and renders the wrapped component with the fresh data!
// Notice that we pass through any additional props
return <WrappedComponent data={this.state.data} {...this.props} />;
}
};
}
HOC并沒有修改輸入的組件,也沒有使用繼承去重用它的行為。HOC只是一個(gè)函數(shù)。wrapped 組件接受了容器的所以props,同時(shí)還接受了一個(gè)新的prop(data),data用于渲染wrapped 組件的輸出。HOC不關(guān)心數(shù)據(jù)怎么使用也不關(guān)心數(shù)據(jù)為什么使用,wrapped組件不關(guān)心數(shù)據(jù)是哪兒得到。
因?yàn)閣ithSubscription只是一個(gè)常規(guī)的函數(shù),你能添加任意個(gè)數(shù)的參數(shù)。例如,你能讓data prop的名字是可配置的,從而進(jìn)一步將HOC與wrapped組件隔離。
或者接受一個(gè)配置shouldComponentUpdate,或者配置數(shù)據(jù)源的參數(shù)
使用高階組件時(shí)有些需要注意的地方。
1.不要修改原始組件,這一點(diǎn)很重要
有如下例子:
function logProps(InputComponent) {
InputComponent.prototype.componentWillReceiveProps = function(nextProps) {
console.log('Current props: ', this.props);
console.log('Next props: ', nextProps);
};
// The fact that we're returning the original input is a hint that it has
// been mutated.
return InputComponent;
}
// EnhancedComponent will log whenever props are received
const EnhancedComponent = logProps(InputComponent);
這里存在一些問題,1.輸入的組件不能與增強(qiáng)的組件單獨(dú)重用。2.如果給EnhancedComponent應(yīng)用其他的HOC,也會改變componentWillReceiveProps。
這個(gè)HOC對函數(shù)類型的組件不適用,因?yàn)楹瘮?shù)類型組件沒有生命周期函數(shù)HOC應(yīng)該使用合成代替修改——通過將輸入的組件包裹到容器組件中。
function logProps(WrappedComponent) {
return class extends React.Component {
componentWillReceiveProps(nextProps) {
console.log('Current props: ', this.props);
console.log('Next props: ', nextProps);
}
render() {
// Wraps the input component in a container, without mutating it. Good!
return <WrappedComponent {...this.props} />;
}
}
}
這個(gè)新的logProps與舊的logProps有相同的功能,同時(shí)新的logProps避免了潛在的沖突。對class類型的組件和函數(shù)類型額組件同樣適用。
2.不要在render方法中使用HOCs
React的diff算法使用組件的身份去決定是應(yīng)該更新已存在的子樹還是拆除舊的子樹并裝載一個(gè)新的,如果從render方法中返回的組件與之前渲染的組件恒等(===),那么React會通過diff算法更新之前渲染的組件,如果不相等,之前渲染的子樹會完全卸載。
render() {
// A new version of EnhancedComponent is created on every render
// EnhancedComponent1 !== EnhancedComponent2
const EnhancedComponent = enhance(MyComponent);
// That causes the entire subtree to unmount/remount each time!
return <EnhancedComponent />;
}
在組件定義的外部使用HOCs,以至于結(jié)果組件只被創(chuàng)建一次。在少數(shù)情況下,你需要動態(tài)的應(yīng)用HOCs,你該在生命周期函數(shù)或者構(gòu)造函數(shù)中做這件事
3.靜態(tài)方法必須手動復(fù)制
有的時(shí)候在React組件上定義靜態(tài)方法是非常有用的。當(dāng)你給某個(gè)組件應(yīng)用HOCs,雖然原始組件被包裹在容器組件里,但是返回的新組件不會有任何原始組件的靜態(tài)方法。
// Define a static method
WrappedComponent.staticMethod = function() {/*...*/}
// Now apply an HOC
const EnhancedComponent = enhance(WrappedComponent);
// The enhanced component has no static method
typeof EnhancedComponent.staticMethod === 'undefined' // true
為了讓返回的組件有原始組件的靜態(tài)方法,就要在函數(shù)內(nèi)部將原始組件的靜態(tài)方法復(fù)制給新的組件。
function enhance(WrappedComponent) {
class Enhance extends React.Component {/*...*/}
// Must know exactly which method(s) to copy :(
// 你也能夠借助第三方工具
Enhance.staticMethod = WrappedComponent.staticMethod;
return Enhance;
}
4.容器組件上的ref不會傳遞給wrapped component
雖然容器組件上的props可以很簡單的傳遞給wrapped component,但是容器組件上的ref不會傳遞到wrapped component。如果你給通過HOCs返回的組件設(shè)置了ref,這個(gè)ref引用的是最外層容器組件,而非wrapped 組件
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
react-beautiful-dnd 實(shí)現(xiàn)組件拖拽功能
這篇文章主要介紹了react-beautiful-dnd 實(shí)現(xiàn)組件拖拽功能,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-08-08
React Native按鈕Touchable系列組件使用教程示例
這篇文章主要為大家介紹了React Native按鈕Touchable系列組件使用教程示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-11-11
在react-router4中進(jìn)行代碼拆分的方法(基于webpack)
這篇文章主要介紹了在react-router4中進(jìn)行代碼拆分的方法(基于webpack),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-03-03
react學(xué)習(xí)筆記之state以及setState的使用
這篇文章主要介紹了react學(xué)習(xí)筆記之state以及setState的使用,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-12-12
react拖拽組件react-sortable-hoc的使用
本文主要介紹了react拖拽組件react-sortable-hoc的使用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-02-02
關(guān)于?React?中?useEffect?使用問題淺談
本文主要介紹了關(guān)于React中useEffect使用問題淺談,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06

