React 程序之复用与封装

封装与复用是一个经典的话题,在写码的第一天里就听说过这个词,然而在实践生产中,大部分写码的同学没有注意到「封装与复用」的力量。

 

诚然,低效的工作方式是我们加班加点最主要的原因之一,我们或许不能改变「领导的无能」、「频繁的需求改动」甚至是生活的不幸,但是我们能够把我们能改变的做好,这往往就非常磨练一个人了。

 

那么究竟如何去封装 React 中的逻辑、组件、以及各种事务呢?今天我们就来讨论讨论。

 

前置条件

React 中封装的主要目的一定要明确:用更少的代码完成更多的事情。这也就是为什么我提倡 250 行极其以下一个 js 文件是最为合理的方式,在大量实践过程中我发现,超过 250 行代码就证明我的代码会有很大的优化空间。

 

有的人问我什么时候应该抽象,这个问题其实非常的简单: 当你使用 copy 功能时,你马上就要停手,思考一个问题:“我为什么要复制代码?” 这背后的答案往往都是: 有重复的逻辑了。

 

当然,在写程序时,人是最不可靠的,那么我就在思考,有没有一种机制能够自动帮你检查出项目代码中到底有多少「愚蠢、不 DRY」的代码呢?我自己发明的一个理论:互联网现在那么发达,你遇到的问题可能几百年前就被人遇到过并且解决了,所以你要做的只是需要会搜索!

根据这个理论,我搜索了 Github ,果不其然,发现了一个比较优秀的 JS 库:danielstjules/jsinspect,这款库的作用大概就是检测出你代码中的重复率,并且输出出来,非常的有意思。我们可以写一个脚本,在 pre-commits 阶段代码进行检测,然后生成「重复性代码」的报表,让我们在做 code review 时,更加轻松:

哈哈哈,有能力的同学可以封装成一个对比库

 

Render Props VS HOC

很多人在说, React 代码复用机制用的 HOC 就很简单,但是在实践过程中,HOC 并没有想象中的美好,为什么呢,我们来看一个最常见的例子:

熟练 React 的同学一定对这个不陌生,这是使用了 Redux + React-Redux 库做成的一个高阶组件,其作用非常简单的将 redux 中的 state 和 dispatch 发送到组件之中,这种模式其实是复用了 Redux 中的行为:

  1. 把组件与 redux 连接
  2. 将 redux store 注入组件中

因此,HOC 作为「行为抽象」是非常好用的,在实践中,很多行为都可以被抽象成一个 HOC

HOC 例子

很多页面都会在 ComponentDidMount 中 fetch 数据,数据到来的时候做处理,解析,然后输入到组件中去。过程是:

  1. 定一个一组 state 用来接受后端返回的数据
  2. ComponentDidMount 中 fetch 数据,then 之后 setState,catch 的话证明出错,有时候很值会有 race 情况,那么再做另行处理

这是一种「请求-处理」的行为逻辑,我们可以预见到这种逻辑非常的通用(因为很多组件,很多页面都会这么去搞)写一个 HOC 能够非常容易复用这种行为

不得不说,这种搞法在 React 中并不美观,当然比起远古时代工厂方法提供的 mixin 要好看多了。

 

这种封装形式不雅观的原因是,我们只看 Hello 的时候,并不知道 Data,isLoading 是哪里来的,对于不熟悉项目的同学来说,这种形式比较让人懵逼。好处就是不破坏组件本身的内部逻辑和标签。

 

因此,想要不破坏组件本身逻辑的情况下增强组件功能,那么 HOC 将是一种非常好的行为抽象模式,我列举几个比较常见而且使用 HOC 比较好的情况:

  1. 一个组件在外部调用的时候,不添加任何属性就直接使用,如`<App />`这样的调用
  2. 在逻辑已经写好的情况下,你需要改变组件的行为,但是又不想进行组件的 JSX tag 调整
  3. 条件判断类,例如一个组件不改变任何内部情况下,判断是否渲染

 

Render Props 例子

HOC 和 render-props 作为 react 中的代码复用方式,都得到了广泛的使用,那他们的区别是什么?运用的场景又有什么区别?

 

实际上他们的区别并不太明显,在大多数情况下, render props 和 hoc 是可以互换的,那么他们的主要区别在于:

  • hoc 是粗粒度的代码复用,即用于扭转整个组件的行为而不费吹灰之力,在外部调用和内部使用来说,不会造成任何影响
  • render props 是细粒度的代码复用,例如某个 render 函数里的某个组件依赖一些属性和方法的时候,可以考虑使用 render props

 

 

我们来看一下一个简单的例子:

几个月前,我写了一个拖拽组件,Foveluy/Luy-dragger,其用法很简单

虽然我们通过写一个组件(封装组件其实也是一种 HOC),来复用我们的拖拽行为,但是实际上我们发现很多时候很多东西无法复用。

  • 如果我想获取当前组件的拖拽 x,y 轴怎么办?
  • 如果我想获取当前组件是否被点击,我要怎么办?
  • 如果我想为拖拽的组件,指定拖拽区域怎么办

 

不妨写几个例子,一一实现:

累得不行了,作为使用者,我只是想简单的获取一下其中的几个关键属性,我就要写那么多无聊的代码,还要 setState,性能也是太蠢了。

我们使用 renderProps 来封装我们的组件以后,你看,我们的代码就极其的优雅,量少,当然你可以为组件写一个更大的 HOC 去复用诸如 onStart xxx 等逻辑,但是这么做并不优雅,在我看来,这种是蠢笨的。

 

Render Props 例子2

上面,我们看到了一个非常牛逼的封装方式,那就是细粒度的使用 render props 去封装复用我们的代码,那这一个例子就是来看看具体是怎么封装和减少代码的:

这个代码和之前的如出一辙的蠢啊

 

我们将 handleShow 等 xxx 组件进行抽离到一个组件中:

 

使用的时候,我们可以很轻松的细粒度的复用我们的 Modal 点击行为

 

同样,比如打勾,输入框变换,表单组件等等类似的行为,我们都可以抽象进入 render props.

 

 

总结

  1. 使用 danielstjules/jsinspect 检测我们重复的代码,可以在 pre-commit 的时候进行检测,具体的操作,可维护的 React 程序之项目结构梳理 我之前的一篇文章
  2. HOC 适用于大粒度的组件复用
  3. render props 适用于小粒度的组件复用
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值