You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
前面提到过React官方文档对于高阶组件的使用注意,而绕开它最好的办法就是使用renderProps,React-Router作者Michael Jackson有一个演讲视频《Never Write Another HoC》。首先我们需要明白Modal本身就是用renderProps模式写的,但这里为了演示,修改一下,用renderProps模式重写弹框容器组件,改动其实很小:
写于:2019-02-04
吐槽大会
刚接触React时,新鲜感爆棚,原来前端代码可以这样写,页面可以这样搭,事件可以这样绑定,一切的一切都是这么让人好奇。但在公司做了好几个中后台系统以后,发现自己写出的代码千篇一律,做出的页面就像多胞胎,随意从工作中截了三个页面:
我工作主要围绕着React,Dva,Antd这一类框架展开,yes, you are right, 这一套组合就是为中后台系统而生的,复制粘贴,不断的重复,真的就是感觉自己在搬砖,做着体力劳动,如果日复一日的这样下去,我估计30岁就会被退(gun)休(dan),不是被公司,而是被这个圈子。
前端练习生
组件化的再封装
当你用着Antd的各种组件(Form, Table, Row,Button等等),省去了担忧写页面样式的烦恼,但总感觉自己干了很多重复工作,比如渲染一个列表,你需要做如下的配置,N个列表页,这样的代码你要写N次;
你需要配置分页相关属性,翻页后调用的方法,数据源和表格项,但对于同一个中后台系统来说,他们的数据结构非常相似,各个表之间唯一不同的就是数据及表格项。所以我们可以在Table组件的基础上再封装一层变成一个EnhanceTable,然后在这个组件里加上这些通用的数据处理。使用时,我们只需要直接将整个props传递过去(虽然有一点浪费性能),附带设置一下rowKey属性,详情源码及使用请可参考示例项目。
<EnhanceTable {...this.Props} rowKey="id" extraFields={this.getExtraFields()} />
组件化的进阶:配置对象搭页面
以前我写上面一个页面,是这样挨着一行行敲代码的。当然也不全是,ctrl + c,ctrl + v这样的基本技能也是必知必用的:
当后面自己写多了,厌烦了FormItem, getFieldDecorator, Input,Select这些组件之后,代码就变成了这样:
解决的办法就是组件的封装加配置,详情源码及使用请参考示例项目代码:
学以致用,才能让工作更简单:高阶组件
当明白了keys,状态提升,props,state这些概念后,好像已足够让我们完成产品需求中的页面,前面组件化的封装其实仅仅仅复用了数据处理的逻辑,但有些需求,普通的组件化封装已经不足以解决,比如下面这种:
你一个页面有多个弹框,也许不止上面这三种,有可能十多种,刚开始工作时,我是这样写的:
但当你的页面弹框有十多种时,条件表达式就显得有点无助了,可能需要if...else,或者Switch...case。但是在判断页面的动作时,可能你已经用过相似的判断逻辑,所以你的代码也许可以精简一下了。其实上面的操作,我们想做的,就是为我们想要显示的组件加一个弹框容器。盆友,高阶组件了解一下,官方文档是这样描述的
用通俗的话来讲,经过高阶组件(函数)higherOrderComponent强化过的组件WrappedComponent ,新组件(EnhancedComponent)除拥有原始组件的特性外,还会拥有一些额外的能力,比如这里我们想实现的弹框容器。Redux-Router的connect就是最常见的高阶函数,它让展示组件拥有了方法和状态,还有Form.create():
接下来,我们试着来实现这个能给普通组件加一个弹框容器的高阶组件:
然后调用时,你只需要在判断动作(type)的时候,同时指定想对应的子组件,然后再这样调用:
要想写一个好用的高阶组件,看起来很简单,但实际上需要考虑的细节很多,就拿上面没有加入缓存的代码来说,会产生如下图所示的效果。
探究其原因,在点击提交时,因为子组件调用了父组件的方法,改变了confirmLoading的状态,会导致父组件render方法的执行,然后const WithModal = withModal(child)会再执行一次。所以WithModal已不再是点击ok前的那个WithModal了,componentWillReceiveProps就不再适用了。所以要想保持组件原有的生命周期,我们就需要避免WithModal组件被销毁,所以使用了缓存的思路来保持这个组件。其实在官方文档中,已经特别提到了一般而言,你不需要考虑这些细节东西。但是它对高阶函数的使用有影响,那就是你不能在组件的render函数中调用高阶函数, 但实际使用时,我们有这种需求确实要用,我们就得想办法绕过这些坑,详细实现可参考示例项目代码。
能用高阶组件实现的,RenderProps都可以代替
前面提到过React官方文档对于高阶组件的使用注意,而绕开它最好的办法就是使用renderProps,React-Router作者Michael Jackson有一个演讲视频《Never Write Another HoC》。首先我们需要明白Modal本身就是用renderProps模式写的,但这里为了演示,修改一下,用renderProps模式重写弹框容器组件,改动其实很小:
what,好像并没有改变什么,和最开始Modal的直接调用并没有多大的差别,所以有没有更好的办法呢?有,就是与高阶组件结合:
有可能你会问,这里也在render中使用了高阶组件(enhanceComponent),那不是也会造成子组件的重复销毁与生成,没法保持组件完整的生命周期吗?答案是否,子组件的生命周期不会被打断,因为return component;并没有重新生成一个组件,它只是改变了组件的地址指向,因为子组件是一个引用类型,而不是一个基本类型,所以render函数多次执行,只要动作是同一种,那上一次被挂起的子组件将被沿用。renderProps确实是一种很值得实践的模式,值得深究,在Graphql的Apollo框架中,这种模式被最为推荐。
后记
以上就是我工作半年,自己慢慢学习和琢磨的中后台系统开发的最佳实践。React相比于Vue和Angular,它确实要灵活好多,有多种设计模式,只要你能想,有思路,就没有没法实现的。文中所提到的所有代码都可以在示例项目中找到,并npm i,npm start跑起来:
Github:示例项目
The text was updated successfully, but these errors were encountered: