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
exportfunctionsetIn(obj: any,path: string,value: any): any{letres: any=clone(obj);// this keeps inheritance when obj is a classletresVal: any=res;leti=0;letpathArray=toPath(path);for(;i<pathArray.length-1;i++){constcurrentPath: string=pathArray[i];letcurrentObj: any=getIn(obj,pathArray.slice(0,i+1));if(currentObj&&(isObject(currentObj)||Array.isArray(currentObj))){resVal=resVal[currentPath]=clone(currentObj);}else{constnextPath: string=pathArray[i+1];resVal=resVal[currentPath]=isInteger(nextPath)&&Number(nextPath)>=0 ? [] : {};}}// Return original object if new value is the same as currentif((i===0 ? obj : resVal)[pathArray[i]]===value){returnobj;}if(value===undefined){deleteresVal[pathArray[i]];}else{resVal[pathArray[i]]=value;}// If the path array has a single element, the loop did not run.// Deleting on `resVal` had no effect in this scenario, so we delete on the result instead.if(i===0&&value===undefined){deleteres[pathArray[i]];}returnres;}
背景
测试反馈页面在录入信息时很卡,甚至浏览器偶尔还会crash。
原因
利用React Dev Tools发现
values.touched
属性值有点问题:居然是个长度很大的数组,但我期望的结构是
{ "xxxSet": {"53656222": true }}
,即53656222
只是数字命名的对象属性,不是数组的索引。看来得阅读下FormitsetFieldTouch
源码看看具体逻辑。setFieldTouch
内部调用FormiksetIn
:内部依赖了lodash的
clone
,toPath
等函数。并且在实现里可以看到如果path
是数字就生产数组了:如果数字命名的属性的数字非常大,则误产生的数组会导致
clone
函数很慢,甚至造成out of memery。setIn
是个Util函数,SET_FIELD_VALUE
和SET_FIELD_ERROR
也会依赖这个函数,他们也是存在类似问题。解决方案
这个应该是Formik的Bug,好些Issues都些涉及这个问题Filter by is:issue is:open Numeric ,但至今还没解决该问题,只能采用其他方式绕过这个问题了。
解决方案1
那不用数字命名的属性呗,比如给数字命名的属性加个前缀。
解决方案2
在初始化
Formit
时手动传initialTouched
对象,即明确touched
的结构,这样也可以绕过setIn
里根据属性名字类型自动生成对象和数组的逻辑resVal = resVal[currentPath] = isInteger(nextPath) && Number(nextPath) >= 0 ? [] : {};
。比如针对上面提到的问题可以改成这样:
这样
touched.dataSet
的类型就明确是个对象了,Formik不会误生成数组。同样的也同时指定下initialErrors
对象。注意⚠️ :
这个解决方案存在一个问题。
state.values/touched/errors
存在覆盖赋值的场景(如下),此时要保证数字命名的属性结构不变,否则会导致setIn
时无法获取指定的结构。还是别用这个方案了吧,需要注意的点太多。
解决方案 Next (TODO)
何不让开发指定数字命名的属性是属于数组还是对象呢?比如采用约定大于配置的原则这样规定:
a[123].age
则表示a
是个数组a.123.age
则表示a
是个对象参考
The text was updated successfully, but these errors were encountered: