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

React设计模式与场景分析

时间:2023-04-02 23:45:18 HTML

本周连续发表了两篇关于React的文章:组件重用的那些事儿——React实现轮子的按需加载React应用设计——CurryMagicUse涉及到React组件重用,轮子设计相关话题,并结合相关场景实例进行分析。这些内容都是React设计模式。谈到设计模式,读者不必害怕。其实这就是React开发应用灵活性的体现。在今天的文章中,我们继续通过一个场景,一步一步,通过一步步优化设计来加深理解。场景介绍界面左侧大面积显示区块内容,点击继续按钮可切换到下一条内容信息;右侧是导航栏,表示当前块显示的信息项。如果看Gif图片还不够,可以去CodeSandbox在线了解一下。具体代码结构为:classAppextendsComponent{render(){return();}}Stepper组件的'stage'属性表示默认的起始块,也使用与'stage''state相同的名称。这里的舞台代表左边的内容块。handleClick方法切换this.stata.stage。classStepperextendsComponent{state={stage:this.props.stage}staticdefaultProps={stage:1}handleClick=()=>{this.setState({stage:this.state.stage+1})}render(){const{stage}=this.state;return(

);我们看到Stepper组件包含Progress组件(左侧导航)和Steps组件。这样的代码运行良好,但在可重用性和灵活性方面存在一些问题。例如:如果我们需要切换Progress和Steps组件的显示顺序(左右)怎么办?如果我们的步进器需要承载更多阶段怎么办?如果我们需要更改某个阶段的内容怎么办?如果我们想切换阶段的顺序怎么办?基于现有代码,这些问题都可以解决。但是需要重新更改组件写入内容。如果哪天增加了新的或调整的需求,组件内容也需要重写。接下来,我们用另一种方式来实现需求,让代码更加灵活和可重用。重新设计仔细观察Stepper组件:它包含当前块阶段,以及更改阶段的方法,渲染两个子组件。我们使用FunctionasChildComponent来重构Stepper组件。(如果对FunctionasChildComponent不熟悉,可以参考我之前的文章ComponentReuseThings-ReactImplementsLoadingWheelsonDemand)如下图:Progress和Steps组件不再直接出现在组件的render方法中步进器组件。我们使用this.props.children渲染Stepper组件的所有子组件。这样,Stepper组件渲染的内容就更加灵活了。但是,仅靠这样的修改是不可能完成需求的。当用户点击继续按钮时,舞台不会切换。因为Progress和Steps组件不再通过props感知stage和handleClick方法。为了解决这个问题,我们可以手动遍历Stepper组件的子节点,将对应的props一一注入。以下代码:constchildren=React.Children.map(this.props.children,child=>{returnReact.cloneElement(child,{stage,handleClick:this.handleClick})})使用React.Children.map来制作childnodes通过React.cloneElement方法遍历复制子组件,该方法具有通过第二个参数添加额外props的能力。Stepper组件的render方法只需要具体应用:const{stage}=this.state;constchildren=React.Children.map(this.props.children,child=>{returnReact.cloneElement(child,{阶段,handleClick:this.handleClick})})return({children}
);现在该应用程序再次正常运行!classAppextendsComponent{render(){return(
);我们也可以将相同的方法应用到Progress组件中。这里就不一一展开了。使用静态属性值得一提的是,我们可以使用静态属性来增强代码的可读性。静态属性允许我们直接在类中调用方法。首先,我们在Stepper组件中创建两个静态方法,分别赋值给Progress组件和Steps组件:staticProgress=Progress;staticSteps=Steps现在,在App.js中我们可以直接:importReact,{Component}from'react';importStepperfrom"./Stepper"classAppextendsComponent{render(){return();}}导出默认应用;这样做的好处是不需要反复导入Progress组件和Steps组件,它们都会作为Stepper的静态属性出现。我个人不太喜欢这种方法。使用ReactTransitionGroup我们使用ReactTransitionGroup为Steps组件内容添加过渡动画。只有当props.num与this.props.stage等时,区块内容设置为可见:classStepsextendsComponent{render(){const{stage,handleClick}=this.propsconstchildren=React.Children.map(this.props.children,child=>{console.log(child.props)return(stage===child.props.num&&{child})})return({children}
Continue);}}我们还可以向Steps组件添加任何内容:={1}/>);}}重新设计后,整个应用变得更加灵活,可复用更加灵活,我们可以指定任意数量的stage,每个stage的文本内容也可以自定义。同样,阶段的顺序可以自由搭配。重构后的代码及其效果可以在这里查看。思考和未完待续如果你认为上面的代码是完美无缺的,那显然很简单。要求是可变的。如果我们想在Steps块中添加一个大标题怎么办?classAppextendsComponent{render(){return(
Title
);}}如图,这样一来,Stepper.Steps组件就不再是Stepper组件的直接唯一子节点了,预期的props自然又得不到了!问题还不止于此。我个人不是很喜欢React.cloneElement这样的顶级API。除了我的喜好之外,我还有一个无法回避的问题:使用React.cloneElement展开props时出现props命名冲突怎么办?比如一个遇到一个名为value的prop,后果可想而知。那么问题来了,有没有更优雅高效的方式来解决上述问题呢?或者,是否有更好的方法来实现更灵活的设计?答案一定是肯定的,我会留在下一篇文章中解释。本文来自:HowToMasterAdvancedReactDesignPatterns,部分内容有所改动。广告时间:如果你对前端开发感兴趣,尤其是React技术栈:我的新书,或许有你想看的。关注作者LucasHC,新书出版时有赠书。快乐编码!PS:作者Github仓库和知乎问答链接,欢迎各种形式交流!我的其他关于React技术栈的文章:从setStatepromises的讨论中体会React团队的设计思路React应用设计之道——柯里化组件的神奇用途,重用那些东西——React实现轮子的按需加载通过例子,学写ReactComponents的《最佳实践》React组件设计与分解思维BindthisfromReact,看JS语言开发和框架设计让Uber手机网页版不够极致性能看真章**React+Redux打造“NEWSEARLY”单页应用一个项目看懂最前沿技术栈的真谛