阅读 750

react->Ant Design->form表单控件按需加载实现

最近在折腾react,所以,将一些自己碰到的问题以及对应的解决办法分享出来,期待和大家共同探讨和进步!

概述

react折腾系列之一:如何在ant框架下,Form表单中实现getFieldDecorator包装控件的按需加载。

文笔有限,代码才是程序员的真爱。相信看完下方的几行简单的代码和其中的注释,大家对本文要描述的问题,会有比较清晰的了解。


ps:当然,很多情况下,你可能只需要实现路由组件的按需加载便可,无需涉及这么精细化的小组件操作,那么请不要纠结,本文偏重技术问题探讨,至于应用场景,可灵活自行抉择。

思路

问题清晰了,那么接下来着手解决之前,得有个大概的思路:

  1. 上网查找相关资料
  2. 上github看是否有相关实例参考
  3. 此处省略n中方法
  4. 自行撸代码

针对该问题,其实比较难找到对应的资料和实例(可能确实这样的需求不多吧)。所以只能自行撸代码了,其实粗略想想,应该不难,找个现成的react延迟加载组件还不容易啊。相信这个难不倒大家,接下来以大家比较熟悉的react-loadable为例。

步骤一:react-loadable用起来

代码大致如下,比较简单,也不需要注释了,具体可查看官方文档和实例。


代码撸完,跑起来看看,不看不知道,一看吓一跳。。。

  1. 延迟加载的Input,每次输完一个字符就失焦了,需要再次点击获焦才能输入第二个字符
  2. 延迟加载的Select,选完后,其他的控件(例如Input输入)一操作,该Select有闪烁效果
  3. 偶现的Form重置失效
  4. 。。。
完全不能接受是不是

必须解决是不是

so,react-devtools看一下:



看起来,一切正常,该有的props和state都有啊!

好吧,没办法,又找不到现成的资料和项目参考。看来只能放大招了,看源码!


上面,是react-loadable的一部分源码。所以,ant中的getFieldDecorator包装后,一旦Form中的任何一项组件(Input之类的)值改动(onchange),Form要更新,便会引起所有组件重新生成。

对应到react-loadable的代码,便是:任何一个控件(例如Input)触发onchange,Form更新,所有控件重新进constructor,执行对应的init,然后render,第一次render进第一个if,第二次render进第二个if。

验证很简单,在react-loadable的render方法中,把state打印出来:

this.state-- {error: null, pastDelay: true, timedOut: false, loading: true, loaded: null}
this.state-- {error: null, pastDelay: true, timedOut: false, loading: false, loaded: Module}复制代码

重点看loading和loaded字段,每次onchange都会经历一次loading从true到false的过程。

看完react-loadable源码,问题理清楚后,大招不能停,解决思路:

  1. 不使用getFieldDecorator,自行添加onchange实现相关逻辑
  2. 封装react-loadable,避免每次重走constructor->load
  3. react-loadable本身不适合此种场景,重新找一个,或者自己造一个轮子
方法一,有兴趣自行实践,纯属理论分享,笔者自己也没折腾过!

步骤二:react-loadable封装

代码如下,挺简单的改动,每次加载完的组件,存起来,重复使用。

虽然react-loadable也进行了缓存,但是只是进行了网络请求缓存,每次都会重新走load流程,只是第二次过程很快而已,而解决的思路是直接进行组件缓存。

由于掘金的富文本编辑器复制后换行失效,如果需要看此处源码的,可以点击此处查看


步骤三:自己造轮子

可以考虑自己造一个本身就支持组件级别缓存的loadable组件,而且react-loadable本身的源码有三百多行,可能有挺多我们不需要的功能。

  1. 自己造轮子,从三百多行到二十多行
  2. 较好的封装,随意切换(即当你想切回react-loadable的时候,可以低成本切换)

由于掘金的富文本编辑器复制后换行失效,如果需要看此处源码的,可以点击此处查看


用起来也方便:

import loadable from './my-loadable'
function getLoadableComponent(componentName) {
    return loadable(() => import('../form-items/' + componentName), null, null, componentName)
}
export default getLoadableComponent复制代码

码字不易,觉得此文对你有帮助的,麻烦点个赞鼓励鼓励。