1.在React中higher-order component (HOC)是一種重用組件邏輯的高級技術。HOC不是React API中的一部分。HOC是一個函數,該函數接收一個組件并且返回一個新組件。在React中,組件是代碼復用的基本單位。
2.為了解釋HOCs,舉下面兩個例子
CommentList組件會渲染出一個comments列表,列表中的數據來自于外部。
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組件,這個組件用于展示一篇博客信息
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} />;  } } 這兩個組件是不一樣的,它們調用了DataSource的不同方法,并且它們的輸出也不一樣,但是它們中的大部分實現(xiàn)是一樣的:
1.裝載完成后,給DataSource添加了一個change listener
2.當數據源發(fā)生變化后,在監(jiān)聽器內部調用setState
3.卸載之后,移除change listener
可以想象在大型應用中,相同模式的訪問DataSource和調用setState會一次又一次的發(fā)生。我們希望抽象這個過程,從而讓我們只在一個地方定義這個邏輯,然后在多個組件中共享。
接下來我們寫一個創(chuàng)建組件的函數,這個函數接受兩個參數,其中一個參數是組件,另一個參數是函數。下面調用withSubscription函數
const CommentListWithSubscription = withSubscription( CommentList, (DataSource) => DataSource.getComments()); const BlogPostWithSubscription = withSubscription( BlogPost, (DataSource, props) => DataSource.getBlogPost(props.id));
調用withSubscription傳的第一個參數是wrapped 組件,第二個參數是一個函數,該函數用于檢索數據。
當CommentListWithSubscription和BlogPostWithSubscription被渲染,CommentList和BlogPost會接受一個叫做data的prop,data中保存了當前從DataSource中檢索出的數據。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只是一個函數。wrapped 組件接受了容器的所以props,同時還接受了一個新的prop(data),data用于渲染wrapped 組件的輸出。HOC不關心數據怎么使用也不關心數據為什么使用,wrapped組件不關心數據是哪兒得到。
因為withSubscription只是一個常規(guī)的函數,你能添加任意個數的參數。例如,你能讓data prop的名字是可配置的,從而進一步將HOC與wrapped組件隔離。
或者接受一個配置shouldComponentUpdate,或者配置數據源的參數
使用高階組件時有些需要注意的地方。
1.不要修改原始組件,這一點很重要
有如下例子:
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 receivedconst EnhancedComponent = logProps(InputComponent); 這里存在一些問題,1.輸入的組件不能與增強的組件單獨重用。2.如果給EnhancedComponent應用其他的HOC,也會改變componentWillReceiveProps。
這個HOC對函數類型的組件不適用,因為函數類型組件沒有生命周期函數HOC應該使用合成代替修改――通過將輸入的組件包裹到容器組件中。
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} />;  } }} 這個新的logProps與舊的logProps有相同的功能,同時新的logProps避免了潛在的沖突。對class類型的組件和函數類型額組件同樣適用。
2.不要在render方法中使用HOCs
React的diff算法使用組件的身份去決定是應該更新已存在的子樹還是拆除舊的子樹并裝載一個新的,如果從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,以至于結果組件只被創(chuàng)建一次。在少數情況下,你需要動態(tài)的應用HOCs,你該在生命周期函數或者構造函數中做這件事
3.靜態(tài)方法必須手動復制
有的時候在React組件上定義靜態(tài)方法是非常有用的。當你給某個組件應用HOCs,雖然原始組件被包裹在容器組件里,但是返回的新組件不會有任何原始組件的靜態(tài)方法。
// Define a static methodWrappedComponent.staticMethod = function() {/*...*/}// Now apply an HOCconst EnhancedComponent = enhance(WrappedComponent); // The enhanced component has no static methodtypeof EnhancedComponent.staticMethod === 'undefined' // true 為了讓返回的組件有原始組件的靜態(tài)方法,就要在函數內部將原始組件的靜態(tài)方法復制給新的組件。
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返回的組件設置了ref,這個ref引用的是最外層容器組件,而非wrapped 組件
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持武林網。
新聞熱點
疑難解答