antd可编辑单元格实现原理
最近在项目复盘的时候,发现使用了antd可编辑单元格。用的时候没仔细看,现在详细看下实现原理。
antd可编辑单元格:https://ant.design/components/table-cn/#components-table-demo-edit-cell
项目复盘:
js代码
import React, { useContext, useState, useEffect, useRef } from 'react';
import { Table, Input, Button, Popconfirm, Form } from 'antd';
const EditableContext = React.createContext(null);
const EditableRow = ({ index, ...props }) => {
const [form] = Form.useForm();
return (
);
};
const EditableCell = ({
title,
editable,
children,
dataIndex,
record,
handleSave,
...restProps
}) => {
const [editing, setEditing] = useState(false);
const inputRef = useRef(null);
const form = useContext(EditableContext);
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 ? (
) : (
{children}
);
}
return {childNode} ;
};
class EditableTable extends React.Component {
constructor(props) {
super(props);
this.columns = [
{
title: 'name',
dataIndex: 'name',
width: '30%',
editable: true,
},
{
title: 'age',
dataIndex: 'age',
},
{
title: 'address',
dataIndex: 'address',
},
{
title: 'operation',
dataIndex: 'operation',
render: (_, record) =>
this.state.dataSource.length >= 1 ? (
this.handleDelete(record.key)}>
Delete
) : null,
},
];
this.state = {
dataSource: [
{
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',
},
],
count: 2,
};
}
handleDelete = (key) => {
const dataSource = [...this.state.dataSource];
this.setState({
dataSource: dataSource.filter((item) => item.key !== key),
});
};
handleAdd = () => {
const { count, dataSource } = this.state;
const newData = {
key: count,
name: `Edward King ${count}`,
age: '32',
address: `London, Park Lane no. ${count}`,
};
this.setState({
dataSource: [...dataSource, newData],
count: count + 1,
});
};
handleSave = (row) => {
const newData = [...this.state.dataSource];
const index = newData.findIndex((item) => row.key === item.key);
const item = newData[index];
newData.splice(index, 1, { ...item, ...row });
this.setState({
dataSource: newData,
});
};
render() {
const { dataSource } = this.state;
const components = {
body: {
row: EditableRow,
cell: EditableCell,
},
};
const columns = this.columns.map((col) => {
if (!col.editable) {
return col;
}
return {
...col,
onCell: (record) => ({
record,
editable: col.editable,
dataIndex: col.dataIndex,
title: col.title,
handleSave: this.handleSave,
}),
};
});
return (
'editable-row'}
bordered
dataSource={dataSource}
columns={columns}
/>
);
}
}
ReactDOM.render( , mountNode);
分析
class
可以看到这竟然是个class组件
构造函数
// 基本写法
constructor(props) {
super(props);
...
}
this.columns
可以看到在构造函数了虚拟了列数据
this.columns = [
{
title: 'name',
dataIndex: 'name',
width: '30%',
editable: true, // 本列可编辑
},
{
title: 'age',
dataIndex: 'age',
},
{
title: 'address',
dataIndex: 'address',
},
{
title: 'operation',
dataIndex: 'operation',
render: (_, record) => // 这里的第二个参数record指代该行数据
this.state.dataSource.length >= 1 ? ( // 调用了state数据判断,我们看完state和方法再回来。
this.handleDelete(record.key)}>
Delete
// 该列固定战术Delete字符,点击时弹出气泡确认,确认删除时传入该行key调用this.handleDelete方法删除
) : null,
},
];
this.state
在这里模拟了表单数据
this.state = {
dataSource: [
{
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',
},
],
count: 2,
};
方法
handleDelete
删除方法,看多了函数组件看class的setState有点不适应。
很常见的删除原理:比对该行key,然后用 dataSource.filter()方法设置状态
handleDelete = (key) => {
const dataSource = [...this.state.dataSource];
this.setState({
dataSource: dataSource.filter((item) => item.key !== key),
});
};
handleAdd
增加方法。在这里模拟了一条新数据。
handleAdd = () => {
const { count, dataSource } = this.state;
const newData = {
key: count,
name: `Edward King ${count}`,
age: '32',
address: `London, Park Lane no. ${count}`,
};
this.setState({
dataSource: [...dataSource, newData],
count: count + 1,
});
};
handleSave
保存方法。应该是可编辑单元格编辑后调用保存吧。我们可以先去看render(){}内部
handleSave = (row) => {
const newData = [...this.state.dataSource];
const index = newData.findIndex((item) => row.key === item.key);
const item = newData[index];
newData.splice(index, 1, { ...item, ...row });
this.setState({
dataSource: newData,
});
};
render(){}
这里直接逐行注释了
// 从state中解构出表单数据
const { dataSource } = this.state;
// components对象,描述了编辑行EditableRow方法与编辑单元格EditableRow方法
const components = {
body: {
row: EditableRow,
cell: EditableCell,
},
};
// 列数组处理:不可编辑的列直接返回值;可编辑列增加**onCell方法**,该方法返回该单元格数据、editable等属性、以及保存方法
const columns = this.columns.map((col) => {
if (!col.editable) {
return col;
}
return {
...col,
onCell: (record) => ({
record,
editable: col.editable,
dataIndex: col.dataIndex,
title: col.title,
handleSave: this.handleSave,
}),
};
});
// 返回一个增加行按钮、一个table,tabe上有components属性
return (
'editable-row'}
bordered
dataSource={dataSource}
columns={columns}
/>
);
EditableRow
// 创建一个Context对象
const EditableContext = React.createContext(null);
const EditableRow = ({ index, ...props }) => {
const [form] = Form.useForm();
return (
);
};
EditableCell
相关