React——高階組件

分類:IT技術 時間:2017-09-28

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的不同方法,並且它們的輸出也不一樣,但是它們中的大部分實現是一樣的:

1.裝載完成後,給DataSource添加了一個change listener
2.當數據源發生變化後,在監聽器內部調用setState
3.卸載之後,移除change listener

可以想象在大型應用中,相同模式的訪問DataSource和調用setState會一次又一次的發生。我們希望抽象這個過程,從而讓我們只在一個地方定義這個邏輯,然後
在多個組件中共享。

接下來我們寫一個創建組件的函數,這個函數接受兩個參數,其中一個參數是組件,另一個參數是函數。下面調用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=http://www.cnblogs.com/QxQstar/p/{this.state.data} {...this.props} />;
    }
  };
}

 

 HOC並沒有修改輸入的組件,也沒有使用繼承去重用它的行為。HOC只是一個函數。wrapped 組件接受了容器的所以props,同時還接受了一個新的prop(data),data
用於渲染wrapped 組件的輸出。HOC不關心數據怎麽使用也不關心數據為什麽使用,wrapped組件不關心數據是哪兒得到。
因為withSubscription只是一個常規的函數,你能添加任意個數的參數。例如,你能讓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 received
const 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,以至於結果組件只被創建一次。在少數情況下,你需要動態的應用HOCs,你該在生命周期函數或者構造函數中做這件事

3.靜態方法必須手動復制

有的時候在React組件上定義靜態方法是非常有用的。當你給某個組件應用HOCs,雖然原始組件被包裹在容器組件裏,但是返回的新組件不會有任何原始組件的靜態
方法。

 

// 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

 

 為了讓返回的組件有原始組件的靜態方法,就要在函數內部將原始組件的靜態方法復制給新的組件。

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 組件

 


Tags: 組件 handleChange DataSource this comments React

文章來源:


ads
ads

相關文章
ads

相關文章

ad