0%

react-ant-design-component-table-10

介绍

各位老铁大家好,今天我们来学习一下如何实现可编辑表格,可编辑表格是指在用户点击某个单元格时自动进入编辑状态,用户直接在表格中修改数据,当表格是去焦点时,自动保存修改后的数据。

首先,我们来思考一下,应该如何让表格中的单元格可编辑?如果让你实现这个功能,改如何下手呢?

基本的思路就是,用一个变量控制单元格当前的状态(编辑/非编辑),编辑状态下显示输入框供用户编辑,非编辑状态下显示文本内容。这个思路怎么说呢,大差不差,但是有许多的细节需要完善,我们一步一步来吧。

自定义Table渲染器

Table组件写了好几篇了,但是有一个重要的功能始终没有介绍,那就是自定义渲染器,默认情况下,Ant Design会自动渲染Table的每一行,以及每一行中的每一个Cell,但是有时候我们需要自行渲染行和Cell,比如可编辑单元格就需要自定义渲染器。

Table有一个属性叫做components,这个属性可以用来覆盖默认的行和Cell渲染器。我们可以通过这个属性来实现自定义渲染器。

components属性的定义如下,可以看到其中的headerbody属性分别对应表头和表体的渲染器。而body属性又包含rowcell属性,分别对应表体的行和单元格渲染器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
export interface TableComponents<RecordType> {
table?: CustomizeComponent;
header?: {
wrapper?: CustomizeComponent;
row?: CustomizeComponent;
cell?: CustomizeComponent;
};
body?:
| CustomizeScrollBody<RecordType>
| {
wrapper?: CustomizeComponent;
row?: CustomizeComponent;
cell?: CustomizeComponent;
};
}

自定义行渲染器

我们先来实现一个自定义行渲染器,行渲染器最外层用Form包裹,这也这个表格就是一个大的Form,可以方便的进行表单操作。

1
2
3
4
5
6
7
8
const EditableRow: React.FC = ({...props }) => {
const [form] = Form.useForm();
return (
<Form form={form} component={false}>
<tr {...props} />
</Form>
);
};

自定义单元格渲染器

接下来我们来实现一个自定义单元格渲染器,单元格渲染器需要根据当前单元格的状态来决定是显示文本还是输入框。

先定义可编辑Cell的属性

1
2
3
4
5
6
7
8
9
interface EditableCellProps {
title: string;
editable: boolean;
children: React.ReactNode;
dataIndex: string;
record: any;
handleSave?: (record: any) => void;
[key: string]: any;
}

然后定义可编辑Cell组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
const EditableCell = (props: EditableCellProps) => {
const { title, editable, children, dataIndex, record, handleSave, ...restProps } = props;
const [editing, setEditing] = useState(false);
const inputRef = useRef<InputRef>(null);
const form = Form.useFormInstance();

useEffect(() => {
if (editing) {
inputRef.current?.focus();
}
}, [editing]);

const toggleEdit = () => {
setEditing(!editing);
form.setFieldsValue({ [dataIndex]: record[dataIndex] });
};

const save = async () => {
try {
const values = await form.validateFields();

toggleEdit();
handleSave({ ...record, ...values });
} catch (errInfo) {
console.log('Save failed:', errInfo);
}
};

let childNode = children;

if (editable) {
childNode = editing ? (
<Form.Item
style={{ margin: 0 }}
name={dataIndex}
rules={[{ required: true, message: `${title} is required.` }]}
>
<Input ref={inputRef} onPressEnter={save} onBlur={save} />
</Form.Item>
) : (
<div
style={{ paddingInlineEnd: 24 }}
onClick={toggleEdit}
>
{children}
</div>
);
}

return <td {...restProps}>{childNode}</td>;
};

最后定义一个App来调用我们的自定义行和单元格渲染器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
const App: React.FC = () => {
const [dataSource, setDataSource] = useState<DataType[]>([
{
key: '0',
name: 'Edward King 0',
age: '32',
address: 'London, Park Lane no. 0',
},
{
key: '1',
name: 'Edward King 1',
age: '32',
address: 'London, Park Lane no. 1',
},
]);

const defaultColumns = [
{
title: 'name',
dataIndex: 'name',
width: '30%',
editable: true,
},
{
title: 'age',
dataIndex: 'age',
editable: true,
},
{
title: 'address',
dataIndex: 'address',
editable: true,
},
];

const handleSave = (row: any) => {
const newData = [...dataSource];
const index = newData.findIndex((item) => row.key === item.key);
const item = newData[index];
newData.splice(index, 1, {
...item,
...row,
});
setDataSource(newData);
};

const components = {
body: {
row: EditableRow,
cell: EditableCell,
},
};

const columns = defaultColumns.map((col) => {
if (!col.editable) {
return col;
}
return {
...col,
onCell: (record: DataType) => ({
record,
editable: col.editable,
dataIndex: col.dataIndex,
title: col.title,
handleSave,
}),
};
});

return (
<Table
components={components}
bordered
dataSource={dataSource}
columns={columns}
/>
);
};
export default App;