一个小的可编辑表格问题引起的思考
11.21工作中遇到的问题
预期:当每行获取红包金额的时候若出现错误,右侧当行会出现提示
结果:获取红包金额出现错误,右侧对应行并没有出现错误提示
我发现,当我们设置readonly的时候,其实render函数依旧是触发了的,右侧图片中的log被成功打印出来了,可以验证,所以可以粗略说明,records被更改,触发了table,以及当前行的渲染,可为什么提示的数据依旧不变
接下来,我用一个简单的demo也复现了这个问题
将readonly: true替换成editable: false,居然就成功了,这与我之前对于readonly和editable的认知有了太大的差距
扩展:readonly和editable的区别
查找dom元素,发现当我设置editable:false
的时候,包裹tips的dom标签是ant-table-cel,一个表格元素,但是当设置readonly: true
的时候,包裹tips的dom标签是一个表单元素,通过样式class:ant-form-item-control-input-content
,可以猜测目前的包裹tips的dom多半是一个受控的input表单
继而,通过阅读pro component中editabe的源码,找到了关键点,对于受控模式下的组件,需要触发onchange,而在代码中触发表单的onchange的条件,由props.controlled控制,这也是为啥我设置readonly:true的时候,没有看见tips更新,即使表格的datasource更新了
所以得到另一种解决方法,
总结
readonly设置为true,编辑表格的form会收集这个字段的值;但是会变成一个受控的不可编辑的input表单项,若去要更新表单,需要controlled设置为true,这样每次value更新的时候都会重置表单了
若设置 editable: false, form不会收集这个字段值,但是依旧是个table-cell
问题Code如下
import { EditableProTable } from "@ant-design/pro-components";
import { useState } from "react";
import { Columns } from "./columns";
const rowKey = 'productId';
const EditTable = ()=>{
const [data, setData] = useState([
{
productId: '1',
},
{
productId: '2',
},
]);
const [editableKeys, setEditableRowKeys] = useState(() => []);
const [failTips, setFailTips] = useState<any>([]);
const changeFailInfo = (curFailTips: any) =>{
const uniqueFailTips = [...new Set([...failTips, ...curFailTips].map(item => JSON.stringify(item)))].map(item => JSON.parse(item));
setFailTips(uniqueFailTips)
const failList = uniqueFailTips.map((item) => item.productId)
// 将有错误提示的当前行的failTips字段赋值
const temp = JSON.parse(JSON.stringify(data));
failList.forEach((item)=>{
if(data.find((item2:any)=>item2.productId === item)){
temp.find((item2:any)=>item2.productId === item).failTips = uniqueFailTips.find(item2=>item2.productId === item).failDesc
}
})
setData(temp);
}
return (
<>
<EditableProTable
rowKey={rowKey}
columns={Columns(changeFailInfo,failTips)}
scroll={{ x: 1000 }}
bordered
value={data}
recordCreatorProps={false}
editable={{
type: 'multiple',
editableKeys: data.map((item) => item[rowKey]),
onChange: setEditableRowKeys,
}}
tableAlertRender={false}
/>
</>
);
}
export default EditTable
export const Columns = (changeFailInfo: any, failTips: any) => [
{
title: 'productId',
key: 'productId',
dataIndex: 'productId',
field: 'productId',
headerStyle: { fontSize: 14 },
colSize: 0,
width: 100,
readonly: true,
},
{
title: '期望',
key: 'Money',
dataIndex: 'Money',
editor: 'input-editor',
width: 100,
editable: true,
colSize: 1.5,
headerStyle: { fontSize: 14, autoWrapText: true },
fieldProps: (form, { entity, rowKey }) => {
return {
onBlur: async (e) => {
const { productId } = entity;
const value = e.target.value || 0;
if (value == 0) return;
changeFailInfo([{ productId, failDesc: 'tips' }]);
},
};
},
},
{
title: '提示',
key: 'failTips',
dataIndex: 'failTips',
editable: false,
//readonly: true,
align: 'center',
fixed: 'right',
width: 200,
hideInTable: !failTips.length,
renderText: (_: any, record: any) => {
console.log(record, 'yts_change');
return <span style={{ color: 'red' }}>{record.failTips}</span>;
},
},
];