当前位置: 首页 > Web前端 > HTML

你如何使用React高阶组件?

时间:2023-03-27 23:27:25 HTML

HighOrderComponent(封装组件,以下简称HOC),是React开发中提高组件复用性的先进技术。HOC不是React的API,而是基于React特性的开发模型。HOC具体就是接受一个组件作为参数,返回一个新组件的方法constEnhancedComponent=higherOrderComponent(WrappedComponent)在React的第三方生态中有很多用处,比如Redux的connect方法或者React的withrouter方法-路由器。举个例子我们有两个组件://CommentListclassCommentListextendsReact.Component{constructor(props){super(props);this.handleChange=this.handleChange.bind(this);this.state={//"DataSource"是一些全局数据源comments:DataSource.getComments()};}componentDidMount(){//订阅变化DataSource.addChangeListener(this.handleChange);}componentWillUnmount(){//清理监听器DataSource.removeChangeListener(this.handleChange);}handleChange(){//每当数据源改变时更新组件状态this.setState({comments:DataSource.getComments()});}render(){return(

{this.state.comments.map((comment)=>())}
);}}//BlogPostclassBlogPostextendsReact.Component{constructor(props){super(props);this.handleChange=this.handleChange.bind(这个);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;}}它们虽然是两个不同的组件,对DataSource的要求也不同,但是它们有很多相似之处:在组件渲染后监听DataSource,在listener中调用setState,unmout时删除listener在大型项目开发中,这种相似的代码会经常出现,所以如果有一种方法可以将这些相似的代码抽取出来进行复用,可以显着提高项目的可维护性和开发效率。使用HOC,我们可以提供一个方法,它不接受组件和组件之间的一些差异配置作为参数,然后返回一个包装好的组件作为结果。functionwithSubscription(WrappedComponent,selectData){//...并返回另一个组件...returnclassextendsReact.Component{constructor(props){super(props);this.handleChange=this.handleChange.bind(this);this.state={data:selectData(DataSource,props)};}componentDidMount(){//...负责订阅...DataSource.addChangeListener(this.handleChange);}componentWillUnmount(){数据源。removeChangeListener(this.handleChange);}handleChange(){这个。setState({data:selectData(DataSource,this.props)});}render(){//...并用新数据渲染包装的组件!//注意我们传递了任何额外的道具return;}};}参考前端高级面试题的详细解答然后我们就可以通过简单的调用这个方法封装组件:constCommentListWithSubscription=withSubscription(CommentList,(DataSource)=>DataSource.getComments());constBlogPostWithSubscription=withSubscription(BlogPost,(DataSource,props)=>DataSource.getBlogPost(props.id));注意:我们没有修改HOC中的输入组件,没有办法通过继承来扩展组件。HOC通过组合来达到扩展组件的目的。HOC应该是一种没有副作用的方法。在这个例子中,我们提取了两个组件相似的生命周期方法,并提供了selectData作为参数,以便输入组件可以选择它想要的数据。因为withSubscription是一个纯方法,以后如果有类似的组件,可以通过这个方法进行封装,可以省去很多重复的代码。不修改原有组件,使用组合进行功能扩展console.log('下一个道具:',nextProps);};//我们返回原始输入的事实暗示它已经//被改变了。returnInputComponent;}//EnhancedComponent将在收到props时记录constEnhancedComponent=logProps(InputComponent);通过以上方法,我们也可以达到扩展组件的效果,但是会出现一些问题。如果InputComponent本身也有componentWillReceiveProps生命周期方法,则会被覆盖。功能组件不适用,因为在生命周期方法中没有办法修改原始组件。抽象,用户必须知道这个方法是如何实现的,才能避免上面提到的问题。如果我们结合起来做,就可以避免这些问题console.log('下一个道具:',nextProps);}render(){}}}//EnhancedComponent将在收到props时记录constEnhancedComponent=logProps(InputComponent);convention:irr??elevantPropspassedtotheoriginalcomponentHOC组件会在原组件的基础上增加一些扩展功能用到的props,那么这些props不应该传入原组件(当然也有例外,比如HOC组件需要使用原始组件指定的props),一般我们会这样处理props:...passThroughProps}=this.props;//将props注入到被包裹的组件中。这些通常是状态值或//实例方法。constinjectedProp=someStateOrInstanceMethod;//将props传递给包装组件return();}extraProp是HOC组件中要用到的props,其余没有用到的props视为原组件需要用到的props。如果props是两者通用的,可以单独通过约定:packaging组件的显示名称,方便调试functionwithSubscription(WrappedComponent){classWithSubscriptionextendsReact.Component{/*...*/}WithSubscription.displayName=`WithSubscription(${getDisplayName(WrappedComponent)})`;返回WithSubscription;}functiongetDisplayName(WrappedComponent){returnWrappedComponent.displayName||WrappedComponent.name||'Component';}简单的说,通过手动指定displayName,可以更方便的通过reactdevtool观察到HOC组件约定:不要在render方法中调用HOCmethodrender(){//每次创建一个新版本的EnhancedComponentrender//EnhancedComponent1!==EnhancedComponent2constEnhancedComponent=enhance(MyComponent);//这会导致整个子树每次都卸载/重新挂载!return;}首先,每次增强调用都会返回一个新类。React的diffing算法根据组件的特性判断是否需要重新渲染。)完全相等,那么会直接重新渲染,传入props后会diffdeployment,会造成很大的性能损耗。并且重新渲染会导致之前组件的所有状态和子组件丢失。其次,React组件通过props改变它们的显示。不需要每次渲染都动态生成一个组件。理论上,渲染时需要自定义的参数可以通过提前指定props来配置。必须复制静态方法。有时,辅助方法会添加到组件的类中。如果按照上面的方法进行封装,那么封装后的类是不会有这些静态方法的。这时候,为了保持组件使用的一致性,我们一般会将这些静态方法复制到被包装的组件中。functionenhance(WrappedComponent){classEnhanceextendsReact.Component{/*...*/}//必须确切知道要复制哪些方法:(Enhance.staticMethod=WrappedComponent.staticMethod;returnEnhance;}这适用如果你知道输入组件中有静态方法,如果你需要更高的扩展性,你可以选择使用第三方插件hoist-non-react-staticsimporthoistNonReactStaticfrom'hoist-non-react-statics';functionenhance(WrappedComponent){classEnhanceextendsReact.Component{/*...*/}hoistNonReactStatic(Enhance,WrappedComponent);returnEnhance;}refref是React中的一个特殊属性——类似于key,不属于props,即也就是说,我们采用的是传递props的方式,并没有传递ref进去,所以如果此时我们在HOC组件上放一个ref,得到的是打包后的组件,而不是原来的组件,这可能会造成一些问题。