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

React应用设计之道——咖喱魔术

时间:2023-04-05 11:14:41 HTML5

使用React开发应用,给前端工程师无限“组合组装”的乐趣。但在此基础上,组件如何划分、数据流如何流动等应用设计决定了代码层面的美观和健壮性。同时,在React的世界里一提到柯里化,或许很多开发者第一反应就是React-redux库的connect方法。但是,如果没有更灵活的应用,只是机械地停留在这里,未免有些可惜。本文基于一个真实场景,从细节入手,分析柯里化如何化繁为简,更优雅的实现需求。场景介绍需求场景是一个卖食品的电商网站,左边部分是商品筛选栏目,用户可以根据:价格区间、商品年龄、商品品牌进行筛选。相应的产品显示在右侧。如下图所示:作为React开发者,我们知道React是组件化的。第一步是考虑基于UE图的组件拆分。这个过程比较简单直观。我们用下图来表示拆分结果:对应的代码是:的主要实现React基于数据状态,第二步是考虑应用状态。我们暂时不需要关心产品展示结果数据。这里我们主要考虑应用最重要的状态,即过滤条件信息。我们使用一个名为filterSelections的JavaScript对象来表示过滤条件信息,如下所示:filterSelections={price:...,ages:...,brands:...,}该数据需要在Products组件中维护。因为Products组件的子组件Filters和ProductResults将依赖于这个数据状态。Filters组件通过prop接收filterSelections状态,并拆解传递给它的三个过滤器子组件:>

);};}同样,ProductResults组件也接收propsfilterSelections状态来显示相应的产品。对于Filters组件来说,它不仅要接收filterSelections数据,还需要更新这个数据。为此,我们在Products组件中设计相应的处理函数,更新过滤信息,命名为updateFilters,并将这个处理函数作为prop发送给Filters组件:classProductsextendsReact.Component{constructor(props){super(道具);this.state={filterSelections:{price:someInitialValue,ages:someInitialValue,brands:someInitialValue,}}}updateFilters=(newSelections)=>{this.setState({filterSelections:newSelections})};render(){return(
);}}注意这里我们绑定了这个方法。有兴趣的读者可以参考我的另一篇文章:BindingthisfromReact看JS语言开发和框架设计。作为Filters组件,还需要进一步拆分和分发处理功能::新值})};updateAgeFilter=(newValue)=>{this.props.selectionsChanged({...this.props.filterSelections,ages:newValue})};updateBrandFilter=(newValue)=>{this.props.selectionsChanged({...this.props.filterSelections,brands:newValue})};render(){return(
);};}根据selectionsChanged函数,我们通过传递不同类型的参数,设计了updatePriceFilter、updateAgeFilter、updateBrandFilter三个方法,分别传递给PriceFilter、AgeFilter、BrandFilter三个组件。这种方法非常简单,但效果很好。但是在Filters组件中,还有更多的功能,而且这些功能似乎都在做同样的逻辑。如果以后增加一个或多个过滤条件,那么同样数量的“孪生”功能也会被增加。这显然不优雅。什么是柯里化?在分析更优雅的解决方案之前,我们先简单了解一下柯里化是什么。柯里化实际上是一种变换,它将一个函数f变换成f',f'的参数接收原函数f的参数,同时返回一个新的函数f'',f''接收剩余的参数和返回函数f的计算结果。这样的描述无疑是抽象的,我们还是通过代码来理解。这是一个简单的求和函数:add=(x,y)=>x+y;柯里化后:curriedAdd=(x)=>{return(y)=>{returnx+y;}}所以当执行curriedAdd(1)(2)后,得到结果3。curriedAdd(x)函数有一个名字叫做partialapplication,curriedAdd函数只需要原add(X,y)函数的一些参数。柯里化一个常规函数让我们对其执行部分应用。柯里化应用,回到之前的场景,我们设计柯里化函数:selectionType]:newValue,});}};可以进一步简化为:updateSelections=(selectionType)=>(newValue)=>{this.props.selectionsChanged({...this.props.filterSelections,[selectionType]:newValue,})};对于updateSelections的部分应用(即上面提到的部分应用):updateSelections('ages');updateSelections('brands');updateSelections('price');这样做的好处相信大家都明白了。这样,我们的Filters组件就完成了:]:newValue,//新的ES6语法!!:)});}};render(){return(
);};}当然,柯里化并不是解决上述问题的唯一方法。让我们学习另一种比较和消化的方法,updateSelections函数未柯里化版本:updateSelections=(selectionType,newValue)=>{this.props.updateFilters({...this.props.filterSelections,[selectionType]:newValue,});}这种设计使得每个Filter组件:PriceFilter、AgeFilter、BrandFilter都调用自己的updateSelections函数,需要组件自己感知filterSelections的属性名来更新对应的属性。这是一种耦合,完整的实现:);};render(){return(<>this.updateSelections('price',value)}/>this.updateSelections('ages',value)}/>this.updateSelections('品牌',价值)}/>);};}其实我觉得在这种场景下,这两个选项的选择完全可以根据开发者的喜好来决定。综上所述,本文内容比较基础,但从细节入手,展现了将React开发与编写与函数式概念相结合的魅力。本文翻译自此处,部分内容有所改动。广告时间:如果你对前端开发感兴趣,尤其是React技术栈:我的新书,或许有你想看的。关注作者LucasHC,新书出版时有赠书。快乐编码!PS:作者Github仓库和知乎问答链接,欢迎各种形式交流!我的其他关于React技术栈的文章:从setStatepromise的讨论中体会React团队的设计思路从setStatepromise的讨论中体会React团队的设计思路通过examplesReact组件学习编写React组件的“最佳实践”设计和分解思维从ReactBinding这个,看JS语言开发和框架设计做Uber移动web版不够打造极致性能看真篇**React+Redux打造“NEWSEARLY”单页应用A项目了解最前沿技术栈真相**