深远精通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,也会变动component威尔ReceiveProps。

 那里存在有的标题,1.输入的组件无法与提升的组件单独重用。2.即使给EnhancedComponent应用其他的HOC,也会变动component威尔ReceiveProps。
葡京娱乐网,以此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
组件