不同数据管理的意义
state、redux等数据管理都有各自的意义,不要盲目使用state或redux
1. state:react是数据驱动更新,state更新会重新触发render进行页面更新,不要将与页面更新无关的数据也存放在state中,state是驱动数据,不仅仅只是单纯的数据概念
2.redux:redux主要是解决跨多层组件之间的数据共享,使用redux管理数据需要相关的reducer、action、connect、mapStateToProps/mapDispatchToProps等,不得不说引入redux会使得代码可读性降低,在开发中应该尽量少使用redux管理数据,对于跨越一两层组件且共享组件较少的数据利用props传递更加方便快捷(见识过将所有数据放在redux中维护的组件,state几乎都废弃了)
3.Form.create():antd(下面所讲特指3.x版本使用方法) Form组件提供的可独立管理多个表单项数据的机制,能够大大减少人工管理表单的变量与表单项数据处理逻辑,极力推荐,创建form实例通过props注入到组件中,灵活的getFieldDecorator方法可进行零散的收集需要绑定的表单项(甚至可以跨越组件进行收集绑定),在以大量表单为主的中后台系统可以看作是另一种独立的数据状态管理器
组件封装、模块拆分
这是很重要且有意义的,开发中一定要注意避免大组件(数千行),合理对大组件进行模块拆分,不同模块组件尽可能将各自功能内聚,减少对外部的依赖,父组件仅用来处理公共事件与子组件的交互即可
与组件实例无关或关联教少的工具函数/教大静态数据放在组件外部定义(当组件复用时能减少代码运行体积)
数据解构与赋值
避免直接使用 data.a.b.c.d 的方式直接读取值,容易造成取值错误而页面崩溃。使用数据解构时结合赋初始值来避免这种错误(也可使用lodash.get):
&& || 与三目
某些场景使用逻辑运算符 && 和 || 以及三目运算符 替代if else 更简洁
语义化使用map、filter、reduce、some等函数
语义化不仅仅只体现在html标签中,我们的所有代码都应具有相应的语义,从语义化html标签、map等语义函数,甚至到变量命名、组件模块划分、项目目录规划都应在阅读代码时体现相应语义
react中的key
合理改变key强制组件进行刷新,可减少部分代码逻辑
比如hooks中仅在创建时执行一次的useState或者表单的初始值赋值(以props传入的值作为初始值),这些初始值在组件更新期间并不会再次生效(即使依赖数据改变了),按正常操作,我们需要在componentWillReceiveProps或者在useEffect中监听传入的props依赖是否变化来对state数据或表单值进行更改,另一种偷懒方法就是利用key的变化来强制组件重新进行加载,当依赖的props数据改变,在父组件中改变子组件key来强制子组件重新加载
可使用setTimeout将某些操作放入异步任务中延后执行,在开发中可以使用这个骚操作(少用)
注意在js中只有setTimeout与Promise回调才是真正意义上的异步事件,这些事件回调最终会放入浏览器渲染进程下的事件触发线程中去等待js引擎线程执行完毕后再去轮询执行。
对于setState所说的同步异步与上面所说完全不是一回事,这里的异步只是react内部实现的state批量更新机制,其实并不能称为异步,只是给我们的感觉像是异步。
React数据驱动概念
把这两个词拆开来理解:数据与驱动
React中进行驱动的数据容易想到的是state,当然也包含props;驱动可以理解为触发render进行页面更新渲染。后者是框架自动实现的,所以React开发中的关注点是数据。
一个组件在首次进行加载时会走一遍render,再之后该组件实例的state与props更改都会触发render,但state与props的改变并不一定会触发组件的render,因为React为了降低对前后数据比对的性能开销采取了数据的浅比对,仅对前后state或前后props数据的第一层进行浅比较,所以可以进一步理解为是state与props数据对象中的第一层属性数据的在驱动React组件的更新
state更改
react不建议直接修改state,开发中如果直接修改state容易造成bug(特指手动修改嵌套层次较深的state数据),要注意继承PureComponent的组件由于重写了SCU,会对state与props进行浅比对可能会导致SCU返回false而不进行页面更新,如果手动修改了state在setState时需重新申明新数组或对象进行数据更新才能触发页面更新:
大表单数据收集
开发场景:在开发中后台应用时,常常会有收集和回填大表单的需求,新建一个任务时会有很多表单数据需要收集,而且此数据不仅仅限于form等表单项,还可能是其它控件:Table中的数据,而且对于大表单往往需要将模块进行拆分,在最终收集数据时需要收集各个模块中的数据,如何组织代码逻辑能够高效进行各模块的数据收集汇总,我通常有两种方式:1. 通过ref;2.通过自定义表单控件;
1.通过ref:每个子表单组件模块维护各自的表单数据,每次表单控件值改变时将最新的表单值汇总到该组件state中进行保存,定义一个获取并返回该模块所有表单数据的函数,该函数本组件并不调用,而是提供给父组件利用ref来获取该子模块的表单数据,这样在提交整体大表单时父组件可利用ref获取每一个子表单组件的数据进行汇总处理。这种方法在父组件获取各个子表单数据时特别简单粗暴。
2.通过自定义表单控件:这种方式基于antd的Form.create()(FormComp)高阶组件,Form.create创建了一个独立的form实例用来管理使用getFieldDecorator()绑定的表单控件(或者自定义表单控件),其重写了表单控件的onChange事件,并结合每个表单控件的value实现了form实例与各表单控件的双向绑定(form实例可利用setFieldsValue一系列api改变各个表单控件的value,另一方面每个表单控件的改变触发onChange也会改变form实例内保存的相应表单控件的value)。基于以上特性,我们只需将在父组件中通过Form.create创建的form实例提供的getFieldDecorator分发到各个子表单模块中就能自动完成对所有子表单数据收集,在提交时利用form.getFieldsValue就能获取到所有的表单数据。这种方式的优势在于一旦完成各表单项绑定,收集与校验逻辑在表单控件改变时就自动完成了。
数据请求延迟的bug与useEffect中的函数防抖失效原因
场景:对于带有多个筛选条件的Table,当条件改变时就发送request请求table数据,但有时由于网络延迟会使得先请求的request返回数据较晚,导致渲染的table数据不正确
解决方法:使用hook的useEffect监控筛选条件的改变,当条件改变时去就发送请求,为了缓解间隔较短的多次请求因网络延迟造成拿不到最后一次请求的数据,将useEffect内部执行的request函数做下防抖
tip: 防抖只能缓解由于网络延迟造成的bug,即使在网络状况良好情况下,加上防抖也可以减少不必要的请求,也是一种优化
useCallback与useMemo
hooks中使用useCallback与useMemo对数据与函数进行缓存,避免在给子组件传递数据与函数时造成子组件无谓刷新