通过实例了解Render Props回调地狱解决方案
简而言之,只要一个组件中某个属性的值是函数,那么就可以说该组件使用了RenderProps这种技术。听起来好像就那么回事儿,那到底RenderProps有哪些应用场景呢,咱们还是从简单的例子讲起,假如咱们要实现一个展示个人信息的组件,一开始可能会这么实现:
constPersonInfo=props=>(); //调用姓名:{props.name}
如果,想要在PersonInfo组件上还需要一个年龄呢,咱们会这么实现:
constPersonInfo=props=>(); //调用姓名:{props.name}
年龄:{props.age}[>
然后如果还要加上链接呢,又要在PersonInfo组件的内部实现发送链接的逻辑,很明显这种方式违背了软件开发六大原则之一的开闭原则,即每次修改都要到组件内部需修改。
开闭原则:对修改关闭,对拓展开放。
那有什么方法可以避免这种方式的修改呢?
在原生js中,如果咱们调用函数后,还要做些骚操作,咱们一般使用回调函数来处理这种情况。
在react中咱们可以使用RenderProps,其实和回调一样:
constPersonInfo=props=>{ returnprops.render(props); } //使用{ }} />{props.name}
{props.age}
值得一提的是,并不是只有在render属性中传入函数才能叫RenderProps,实际上任何属性只要它的值是函数,都可称之为RenderProps,比如上面这个例子把render属性名改成children的话使用上其实更为简便:
constPersonInfo=props=>{ returnprops.children(props); };{(props)=>( )}{props.name}
{props.age}
这样就可以直接在PersonInfo标签内写函数了,比起之前在render中更为直观。
所以,React中的RenderProps你可以把它理解成js中的回调函数。
React组件的良好设计是可维护且易于更改代码的关键。
从这个意义上说,React提供了许多设计技术,比如组合、Hooks、高阶组件、RenderProps等等。
Renderprops可以有效地以松散耦合的方式设计组件。它的本质在于使用一个特殊的prop(通常称为render),将渲染逻辑委托给父组件。
importMousefrom'Mouse'; functionShowMousePosition(){ return( Position:{x}px,{y}px} /> ) }使用此模式时,迟早会遇到在多个renderprop回调中嵌套组件的问题:renderprops回调地狱。
1.RenderProps的回调地狱
假设各位需要检测并显示网站访问者所在的城市。
首先,需要确定用户地理坐标的组件,像
...}这样的组件进行异步操作,使用GeolocationAPI,然后调用Renderprop进行回调。。 然后用获取的坐标用来近似确定用户的城市:
...}/>,这个组件也叫Renderprop。 接着咱们将这些异步组件合并到
组件中 functionDetectCity(){ return({ return( { if(city==null){ return Unabletodetectcity.; } returnYoumightbein{city}.; }} /> ); }} /> ); } //在某处使用可能已经发现了这个问题:RenderProp回调函数的嵌套。嵌套的回调函数越多,代码就越难理解。这是RenderProp回调地狱的问题。
咱们换中更好的组件设计,以排除回调的嵌套问题。
2.Class方法
为了将回调的嵌套转换为可读性更好的代码,咱们将回调重构为类的方法。
classDetectCityextendsReact.Component{ render(){ return; } renderCoords=({lat,long})=>{ return ; } renderCity=city=>{ if(city==null){ return Unabletodetectcity.; } returnYoumightbein{city}.; } } //在某处使用回调被提取到分开的方法renderCoords()和renderCity()中。这样的组件设计更容易理解,因为渲染逻辑封装在一个单独的方法中。
如果需要更多嵌套,类的方式是垂直增加(通过添加新方法),而不是水平(通过相互嵌套函数),回调地狱问题消失。
2.1访问渲染方法内部的组件props
方法renderCoors()和renderCity()是使用箭头函法定义的,这样可以将this绑定到组件实例,所以可以在
和 组件中调用这些方法。 有了this作为组件实例,就可以通过prop获取所需要的内容:
classDetectCityMessageextendsReact.Component{ render(){ return; } renderCoords=({lat,long})=>{ return ; } renderCity=city=>{ //看这 const{noCityMessage}=this.props; if(city==null){ return {noCityMessage}; } returnYoumightbein{city}.; } }renderCity()中的this值指向
组件实例。现在就很容易从this.props获取noCityMessage的值。 3.函数组合方法
如果咱们想要一个不涉及创建类的更轻松的方法,可以简单地使用函数组合。
使用函数组合重构DetectCity组件:
functionDetectCity(){ return; } functionrenderCoords({lat,long}){ return ; } functionrenderCity(city){ if(city==null){ return Unabletodetectcity.; } returnYoumightbein{city}.; } //Somewhere现在,常规函数renderCoors()和renderCity()封装了渲染逻辑,而不是用方法创建类。
如果需要更多嵌套,只需要再次添加新函数即可。代码垂直增长(通过添加新函数),而不是水平增长(通过嵌套),从而解决回调地狱问题。
这种方法的另一个好处是可以单独测试渲染函数:renderCoords()和renderCity()。
3.1访问渲染函数内部组件的prop
如果需要访问渲染函数中的prop,可以直接将渲染函数插入组件中
functionDetectCityMessage(props){ return(); functionrenderCoords({lat,long}){ return( ); } functionrenderCity(city){ const{noCityMessage}=props; if(city==null){ return {noCityMessage}; } returnYoumightbein{city}.; } } //Somewhere虽然这种结构有效,但我不太喜欢它,因为每次
重新渲染时,都会创建renderCoords()和renderCity()的新函数实例。 前面提到的类方法可能更适合使用。同时,这些方法不会在每次重新渲染时重新创建。
4.实用的方法
如果想要在如何处理renderprops回调方面具有更大的灵活性,那么使用React-adopt是一个不错的选择。
使用react-adopt来重构
组件: import{adopt}from'react-adopt'; constComposed=adopt({ coords:({render})=>, city:({coords:{lat,long},render})=>( ) }); functionDetectCity(){ return( {city=>{ if(city==null){ return ); }Unabletodetectcity.; } returnYoumightbein{city}.; }}react-adopt需要一个特殊的映射器来描述异步操作的顺序。同时,库负责创建定制的渲染回调,以确保正确的异步执行顺序。
你可能会注意到的,上面使用react-adopt的示例比使用类组件或函数组合的方法需要更多的代码。那么,为什么还要使用“react-adopt”呢?
不幸的是,如果需要聚合多个renderprops的结果,那么类组件和函数组合方法并不合适。
4.1聚合多个渲染道具结果
想象一下,当咱们渲染3个renderprop回调的结果时(AsyncFetch1、AsyncFetch2、AsyncFetch3)
functionMultipleFetchResult(){ return(( ( ( Fetchresult1:{result1} Fetchresult2:{result2} Fetchresult3:{result3} )}/> )}/> )}/> ); }
组件沉浸所有3个异步获取操作的结果,这是一个阔怕回调地狱的情况。 如果尝试使用类组件或函数的组合方法,它会很麻烦。回调地狱转变为参数绑定地狱:
classMultipleFetchResultextendsReact.Component{ render(){ return; } renderResult1(result1){ return( ); } renderResult2(result1,result2){ return( ); } renderResult3(result1,result2,result3){ return( Fetchresult1:{result1} Fetchresult2:{result2} Fetchresult3:{result3} ); } } //Somewhere 咱们必须手动绑定renderprop回调的结果,直到它们最终到达renderResult3()方法。
如果不喜欢手工绑定,那么采用react-adopt可能会更好:
mport{adopt}from'react-adopt'; constComposed=adopt({ result1:({render})=>, result2:({render})=> , result3:({render})=> }); functionMultipleFetchResult(){ return( {({result1,result2,result3})=>( Fetchresult1:{result1} Fetchresult2:{result2} Fetchresult3:{result3} )} ); } //Somewhere在函数({result1,result2,result3})=>{…}提供给
。因此,咱们不必手动绑定参数或嵌套回调。 当然,react-adopt的代价是要学习额外的抽象,并略微增加应用程序的大小。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。