前言
近期花了差不多 1 个月的时间开发项目,包含看板、列表两种类型,从早期磕磕绊绊的页面设计,中间的反复改版,到最终的完成开发,也体会到了很多不一样的点,感受特别深的是与 Vue 开发时的区别,这里简单记一下我的心得体会。
开发方式的改变
对于 Vue 来说,因为有双向绑定,因此习惯将大多数逻辑写在一个组件里,通过 computed
钩子监听多个依赖项的变动,双向数据流通拿到需要的参数值,必要时再配合上 Vuex,便能很好的解决大多数通用表单的问题。
但这个在 React 中是不太吃香的,因为 React 是单向数据流,先不论比较麻烦的值传递问题,仅看 Hook 对参数的依赖,也会造成一些比较麻烦的问题,比如:
折线图列表,每个列表有自己的查询条件,同时也依赖外部传入的查询条件,为它们封装一个组件的话则会有死循环问题,必须拆成俩组件才能较好的处理此情况,而 Vue 则因为不用担心函数刷新触发查询,因此写在一个组件内也没问题。
因此对于 React 来说,组件拆的越细,耦合程度越低,那么使用 Hook 或者 HOC 来拼凑起来会更为方便,而且对于组件优化、逻辑梳理也会更为容易一些。
页面开发模式
对于顶部 Form 表单,底部多个 折线图、表格 这样常见的表单页面(底部图表可能自带子表单项),我总结了一点开发心得,按照这种方式开发,能减少很多写代码时的心智负担,同时也能便捷的解决很多表单场景。
类比 Vue 的 Vuex + Components
的模式,在 React 开发时使用的模式为 CreateContext + Components
,Store 存储的基础内容如下:
1 | // CreateContext 的对象内容 |
此外,还可以使用 CreateContext + useReducer
的模式来做一些复杂的控制逻辑,当然这也是 React 官网推崇的方式。
组件部分的内容可分为以下三步来看:
Form 表单的初始化
顶部 Form 表单部分推荐单独使用一个组件封装,仅向外暴露最终的 form.getFieldsValue
的内容(无需做任何参数处理),这样的好处是给外部留下预查询的一些处理、以及表单内容的处理和缓存。内部初始化可考虑如下步骤:
- 表单设置初始化 loading 状态(Context 中获取),通过 useRef 创建一个空的表单初始化值 initValue;
- 根据项目需要,使用 useEffect 获取表单的异步依赖项(如下拉菜单)
待所有异步依赖获取完后更新
ref.current
的初始化值,然后使用form.resetFields
更新默认选中项目;最后关闭 loading 状态,并根据情况设置是否默认查询(向外触发 onChange);
查询处于 loading 状态时可以考虑使用一些动画来让页面不显得那么突兀。
表单查询&缓存
Form 表单查询得分为两部:预查询&表单查询。
预查询 的目的其一是为了查询前的表单校验,此外则是部分条件的初始化和格式化(如 table 的初始分页信息
、表单查询条件格式化
),这和分页查询等的逻辑是分开的,最后就是查询条件的缓存了;后续的 表单查询 则使用传入的查询条件即可,因为如其它的分页查询、重置查询等都需要依赖缓存内容进行查询,这样整体的逻辑也就拆开了。
查询的逻辑列举如下:
- 点击表单的查询按钮,触发表单查询函数,向外部 emit 出来的参数,并调用 beforeSearch 函数;
- 处理 form 表单参数,初始化
分页
、排序
等参数信息,缓存格式化后的查询条件,设置 loading 状态; - 根据情况触发对应的查询函数(调用查询 或 监听缓存条件查询);
接口参数还依赖自身组件的情况
除了表单查询外,部分接口可能还有自身的查询条件(如:自带日期筛选的图表组件),这种情况其接口触发查询的情况有两种:Form 表单查询 以及 自身筛选条件变更查询。如果直接监听两者则会出现“初始化查询两次”的问题,因此需要做一些优化,这里我总结了一些方法:
- 使用
Ref
设置初始化状态,初始化时不做处理,待后续变更时再做调用。
1 | import React, { useRef, useEffect } from 'react'; |
- 将俩情况拆分为两种处理方式,并通过
useRef
来存储子查询条件:
- Form 表单项变更,通过触发 useEffect 来触发接口函数;(副作用函数)
- 子查询条件变更,触发 onChange 事件,并更新 ref 的自身查询依赖,手动调用接口函数;(仅事件变动触发)
注意:子查询条件仅根据 onChange 事件触发,初始化参数均在子组件设置的时候初始化即可。