深深驾驭React高阶组件,高阶组件

1.在React中higher-order component
(HOC)是一种重用组件逻辑的高端技巧。HOC不是React API中的一有个别。HOC是多少个函数,该函数接收一个零部件何况再次来到贰个新组件。在React中,组件是代码复用的基本单位。

1.在React中higher-order component
(HOC)是一种重用组件逻辑的高端级才干。HOC不是React
API中的一部分。HOC是叁个函数,该函数接收叁个零部件何况重返二个新组件。在React中,组件是代码复用的着力单位。

2.为了表达HOCs,举上面八个例子

2.为了表明HOCs,举上边五个例证

 

CommentList组件会渲染出二个comments列表,列表中的数据来源于表面。

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>
     );
   }
 }
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组件,那几个组件用于浮现一篇博客消息

 接下来是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} />;
   }
 }
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的例外措施,而且它们的出口也分歧,但是它们中的大多数完成是一模一样的:

那五个零部件是分裂等的,它们调用了DataSource的不一样情势,並且它们的输出也不平等,不过它们中的大多数落到实处是一样的:

1.装载完事后,给DataSource增加了三个change listener
2.当数据源产生变化后,在监听器内部调用setState
3.卸载之后,移除change
listener

1.装载完成后,给DataSource增添了一个change listener
2.当数据源产生变化后,在监听器内部调用setState
3.卸载之后,移除change listener

能够虚拟在大型应用中,同样格局的拜见DataSource和调用setState会贰遍又二遍的产生。我们希望抽象那一个进度,进而让大家只在三个地方定义那个逻辑,然后
在八个零部件中国共产党享。

能够想象在巨型应用中,一样形式的会见DataSource和调用setState会贰回又叁回的发出。大家意在抽象那么些历程,进而让大家只在一个地点定义那些逻辑,然后在三个零部件中国共产党享。

接下去我们写一个创制组件的函数,这几个函数接受八个参数,当中八个参数是组件,另三个参数是函数。上面调用withSubscription函数

接下去大家写七个创办组件的函数,这么些函数接受几个参数,当中二个参数是组件,另三个参数是函数。上边调用withSubscription函数

 

const CommentListWithSubscription = withSubscription(

 CommentList,

 (DataSource) => DataSource.getComments()

);



const BlogPostWithSubscription = withSubscription(

 BlogPost,

 (DataSource, props) => DataSource.getBlogPost(props.id)

); 
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代码如下:

 调用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组件不爱惜数据是哪儿得到。

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

因为withSubscription只是二个好端端的函数,你能加多大六个数的参数。举例,你能让data
prop的名字是可配备的,进而尤其将HOC与wrapped组件隔断。

 

照旧接受一个布署shouldComponentUpdate,可能布置数据源的参数

 HOC并不曾退换输入的零部件,也尚未选择持续去重用它的行事。HOC只是二个函数。wrapped 组件接受了容器的之所以props,同期还接受了二个新的prop(data),data
用以渲染wrapped 组件的输出。HOC不尊敬数据怎么使用也不关怀数据为啥使用,wrapped组件不关心数据是哪儿得到。
因为withSubscription只是三个健康的函数,你能增加放肆个数的参数。举例,你能让data
prop的名字是可布署的,进而进一步将HOC与wrapped组件隔开分离。
要么收受三个配置shouldComponentUpdate,只怕配备数据源的参数

利用高阶组件时有一点点须要专一的地点。

行使高阶组件时有一些须要小心的地点。

1.不用改换原始组件,这一点非常重大

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); 
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。

 这里存在部分主题素材,1.输入的零部件无法与增进的零部件单独重用。2.例如给EnhancedComponent应用其余的HOC,也会改换componentWillReceiveProps。
那几个HOC对函数类型的机件不适用,因为函数类型组件未有生命周期函数
HOC应该使用合成替代修改——通过将输入的零件封装到容器组件中。

这几个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} />;

  }

 }

} 
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

 那么些新的logProps与旧的logProps有同样的成效,同偶然间新的logProps防止了神秘的争辨。对class类型的组件和函数类型额组件一样适用。

React的diff算法使用组件的地位去决定是相应更新已存在的子树照旧拆除旧的子树并装载四个新的,如若从render方法中回到的组件与事先渲染的零部件恒等(===),那么React会通过diff算法更新以前渲染的机件,假使不对等,此前渲染的子树会完全卸载。 

2.不用在render方法中使用HOCs

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

} 

React的diff算法使用组件的地位去调节是应该更新已存在的子树照旧拆除旧的子树并装载一个新的,假若从render方法中回到的零部件与前面渲染的零部件恒等(===),
那正是说React会通过diff算法更新以前渲染的零部件,如若不对等,从前渲染的子树会完全卸载。

 在组件定义的表面使用HOCs,以至于结果组件只被创立三遍。在少数情景下,你供给动态的选择HOCs,你该在生命周期函数可能构造函数中做那事

 

3.静态方法务必手动复制

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

某个时候在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 

 在组件定义的表面使用HOCs,以致于结果组件只被创制一遍。在少数情景下,你须求动态的运用HOCs,你该在生命周期函数可能构造函数中做那件事

 为了让重返的组件有原来组件的静态方法,将要在函数内部将原有组件的静态方法复制给新的零部件。

3.静态方法必得手动复制

function enhance(WrappedComponent) {

 class Enhance extends React.Component {/*...*/}

 // Must know exactly which method(s) to copy :(

  // 你也能够借助第三方工具

 Enhance.staticMethod = WrappedComponent.staticMethod;

 return Enhance;

} 

局地时候在React组件上定义静态方法是特别实惠的。当你给有个别组件应用HOCs,即使原始组件被包裹在容器组件里,但是回去的新组件不会有其余原始组件的静态
方法。

 4.容器组件上的ref不会传送给wrapped component

 

固然容器组件上的props能够极粗略的传递给wrapped
component,不过容器组件上的ref不会传递到wrapped
component。假设您给通过HOCs再次来到的零部件设置了ref,那几个ref援用的是最外层容器组件,而非wrapped
组件

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