Last active
September 25, 2020 09:56
-
-
Save iamso1/0dc057813374053575a6752f62b1bf58 to your computer and use it in GitHub Desktop.
antdesign edit table with hook
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { useContext, useState, useEffect, useRef } from 'react'; | |
import { Table, Input, Button, Popconfirm, Form } from 'antd'; | |
const myData = { | |
myData: [ | |
{ | |
key: '1', | |
name: 'John Brown', | |
a: '1', | |
b: '2', | |
c: '3', | |
d: '4', | |
e: '5', | |
age: 32, | |
address: 'New York No. 1 Lake Park', | |
}, | |
{ | |
key: '2', | |
name: 'Jim Green', | |
a: '1', | |
b: '2', | |
c: '3', | |
d: '4', | |
e: '5', | |
age: 42, | |
address: 'London No. 1 Lake Park', | |
}, | |
{ | |
key: '3', | |
name: 'Joe Black', | |
a: '1', | |
b: '2', | |
c: '3', | |
d: '4', | |
e: '5', | |
age: 32, | |
address: 'Sidney No. 1 Lake Park', | |
}, | |
{ | |
key: '4', | |
name: 'Jim Red', | |
a: '1', | |
b: '2', | |
c: '3', | |
d: '4', | |
e: '5', | |
age: 32, | |
address: 'London No. 2 Lake Park', | |
}, | |
], | |
}; | |
const { Column, ColumnGroup } = Table; | |
const EditableContext = React.createContext(); | |
const EditableRow = ({ index, ...props }) => { | |
const [form] = Form.useForm(); | |
return ( | |
<Form form={form} component={false}> | |
<EditableContext.Provider value={form}> | |
<tr {...props} /> | |
</EditableContext.Provider> | |
</Form> | |
); | |
}; | |
const EditableCell = ({ | |
title, | |
editable, | |
children, | |
dataIndex, | |
record, | |
handleSave, | |
...restProps | |
}) => { | |
const [editing, setEditing] = useState(false); | |
const inputRef = useRef(); | |
const form = useContext(EditableContext); | |
useEffect(() => { | |
if (editing) { | |
inputRef.current.focus(); | |
} | |
}, [editing]); | |
const toggleEdit = () => { | |
setEditing(!editing); | |
form.setFieldsValue({ | |
[dataIndex]: record[dataIndex], | |
}); | |
}; | |
const save = async (e) => { | |
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 | |
className="editable-cell-value-wrap" | |
style={{ | |
paddingRight: 24, | |
}} | |
onClick={toggleEdit} | |
> | |
{children} | |
</div> | |
); | |
} | |
return <td {...restProps}>{childNode}</td>; | |
}; | |
const ReportDemo = ({ data = myData }) => { | |
const [pageNum, setPageNum] = useState(1); | |
const [dataSource, setDataSource] = useState(data.myData); | |
const [count, setCount] = useState(2); | |
const handleDelete = (key) => { | |
setDataSource(dataSource.filter((item) => item.key !== key)); | |
}; | |
const handleAdd = () => { | |
const newData = { | |
key: count + 10, | |
name: `Edward King ${count}`, | |
age: 32, | |
address: `London, Park Lane no. ${count}`, | |
}; | |
setDataSource([...dataSource, newData]); | |
setCount((c) => c + 1); | |
}; | |
const handleSave = (row) => { | |
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, | |
}, | |
}; | |
return ( | |
<div style={{ flex: 1 }}> | |
<Button | |
onClick={handleAdd} | |
type="primary" | |
style={{ | |
marginBottom: 16, | |
}} | |
> | |
Add a row | |
</Button> | |
<Table | |
dataSource={dataSource} | |
style={{ height: '100%' }} | |
scroll={{ x: '50vw' }} | |
sticky | |
pagination={{ | |
position: ['bottomLeft'], | |
total: dataSource.length, | |
defaultPageSize: 5, | |
current: pageNum, | |
onChange: setPageNum, | |
}} | |
components={components} | |
loading={false} | |
> | |
<Column | |
width={'10vw'} | |
fixed="left" | |
title="name" | |
dataIndex="name" | |
key="name" | |
onFilter={(value, record) => record.name.indexOf(value) !== -1} | |
filters={[ | |
{ | |
text: 'Joe', | |
value: 'Joe', | |
}, | |
{ | |
text: 'Jim', | |
value: 'Jim', | |
}, | |
{ | |
text: 'Submenu', | |
value: 'Submenu', | |
children: [ | |
{ | |
text: 'Green', | |
value: 'Green', | |
}, | |
{ | |
text: 'Black', | |
value: 'Black', | |
}, | |
], | |
}, | |
]} | |
onCell={(record) => ({ | |
record, | |
editable: true, | |
dataIndex: 'name', | |
title: 'name', | |
handleSave: handleSave, | |
})} | |
/> | |
<Column width={'20vw'} title="a" dataIndex="a" key="a"></Column> | |
<Column width={'20vw'} title="b" dataIndex="b" key="b"></Column> | |
<Column width={'20vw'} title="c" dataIndex="c" key="c"></Column> | |
<Column width={'20vw'} title="d" dataIndex="d" key="d"></Column> | |
<Column width={'20vw'} title="e" dataIndex="e" key="e"></Column> | |
<Column | |
title="Address" | |
dataIndex="address" | |
key="address" | |
width={'20vw'} | |
/> | |
<Column | |
width={'10vw'} | |
fixed="right" | |
title="Age" | |
dataIndex="age" | |
key="age" | |
sorter={(a, b) => a.age - b.age} | |
defaultSortOrder={'descend'} | |
filters={[ | |
{ text: '三十二', value: 32 }, | |
{ text: '四十二', value: 42 }, | |
]} | |
onFilter={(value, record) => record.age === value} | |
/> | |
<Column | |
width={'10vw'} | |
fixed="right" | |
title="operation" | |
dataIndex="operation" | |
render={(text, record) => | |
dataSource.length >= 1 ? ( | |
<Popconfirm | |
title="Sure to delete?" | |
onConfirm={() => handleDelete(record.key)} | |
> | |
<a>Delete</a> | |
</Popconfirm> | |
) : null | |
} | |
></Column> | |
</Table> | |
</div> | |
); | |
}; | |
export default ReportDemo; | |
//function component vs class component | |
// 1. 狀態的使用方法 | |
// => 使用state時候 不用使用this | |
// => 使用更改狀態的方法時 不用使用this | |
// => state改用useState 而不是用在constructor | |
// 2. 方法的呼叫 | |
// => 使用方法時, 不用this |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { useContext, useState, useEffect, useRef } from 'react'; | |
import { Table, Input, Button, Popconfirm, Form } from 'antd'; | |
import reqwest from 'reqwest'; | |
const myData = { | |
myData: [ | |
{ | |
key: '1', | |
name: 'John Brown', | |
a: '1', | |
b: '2', | |
c: '3', | |
d: '4', | |
e: '5', | |
age: 32, | |
address: 'New York No. 1 Lake Park', | |
}, | |
{ | |
key: '2', | |
name: 'Jim Green', | |
a: '1', | |
b: '2', | |
c: '3', | |
d: '4', | |
e: '5', | |
age: 42, | |
address: 'London No. 1 Lake Park', | |
}, | |
{ | |
key: '3', | |
name: 'Joe Black', | |
a: '1', | |
b: '2', | |
c: '3', | |
d: '4', | |
e: '5', | |
age: 32, | |
address: 'Sidney No. 1 Lake Park', | |
}, | |
{ | |
key: '4', | |
name: 'Jim Red', | |
a: '1', | |
b: '2', | |
c: '3', | |
d: '4', | |
e: '5', | |
age: 32, | |
address: 'London No. 2 Lake Park', | |
}, | |
], | |
}; | |
const { Column, ColumnGroup } = Table; | |
const EditableContext = React.createContext(); | |
const EditableRow = ({ index, ...props }) => { | |
const [form] = Form.useForm(); | |
return ( | |
<Form form={form} component={false}> | |
<EditableContext.Provider value={form}> | |
<tr {...props} /> | |
</EditableContext.Provider> | |
</Form> | |
); | |
}; | |
const EditableCell = ({ | |
title, | |
editable, | |
children, | |
dataIndex, | |
record, | |
handleSave, | |
...restProps | |
}) => { | |
const [editing, setEditing] = useState(false); | |
const inputRef = useRef(); | |
const form = useContext(EditableContext); | |
useEffect(() => { | |
if (editing) { | |
inputRef.current.focus(); | |
} | |
}, [editing]); | |
const toggleEdit = () => { | |
setEditing(!editing); | |
form.setFieldsValue({ | |
[dataIndex]: record[dataIndex], | |
}); | |
}; | |
const save = async (e) => { | |
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 | |
className="editable-cell-value-wrap" | |
style={{ | |
paddingRight: 24, | |
}} | |
onClick={toggleEdit} | |
> | |
{children} | |
</div> | |
); | |
} | |
return <td {...restProps}>{childNode}</td>; | |
}; | |
const ReportDemo = () => { | |
const [pageNum, setPageNum] = useState(1); | |
const [dataSource, setDataSource] = useState([]); | |
const [count, setCount] = useState(2); | |
const [pagination, setPagination] = useState({ | |
current: 1, | |
pageSize: 10, | |
}); | |
const [loading, setLoading] = useState(false); | |
useEffect(() => { | |
fetchData({ pagination }); | |
}, []); | |
const handleDelete = (key) => { | |
setDataSource(dataSource.filter((item) => item.key !== key)); | |
}; | |
const handleAdd = () => { | |
const newData = { | |
key: count + 10, | |
name: `Edward King ${count}`, | |
age: 32, | |
address: `London, Park Lane no. ${count}`, | |
}; | |
setDataSource([...dataSource, newData]); | |
setCount((c) => c + 1); | |
}; | |
const handleSave = (row) => { | |
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 getRandomuserParams = (params) => { | |
return { | |
results: params.pagination.pageSize, | |
page: params.pagination.current, | |
...params, | |
}; | |
}; | |
const handleTableChange = (pagination, filters, sorter) => { | |
console.log('handleTableChange', pagination); | |
fetchData({ | |
sortField: sorter.field, | |
sortOrder: sorter.order, | |
pagination, | |
...filters, | |
}); | |
}; | |
const fetchData = (params = {}) => { | |
setLoading(true); | |
reqwest({ | |
url: 'https://cors-anywhere.herokuapp.com/https://randomuser.me/api', | |
method: 'get', | |
type: 'json', | |
data: getRandomuserParams(params), | |
}).then((data) => { | |
setLoading(false); | |
setPagination({ ...params.pagination, total: 200 }); | |
setDataSource(data.results); | |
}); | |
}; | |
const components = { | |
body: { | |
row: EditableRow, | |
cell: EditableCell, | |
}, | |
}; | |
return ( | |
<div style={{ flex: 1 }}> | |
<Button | |
onClick={handleAdd} | |
type="primary" | |
style={{ | |
marginBottom: 16, | |
}} | |
> | |
Add a row | |
</Button> | |
<Table | |
dataSource={dataSource} | |
style={{ height: '100%' }} | |
scroll={{ x: '50vw' }} | |
sticky | |
pagination={pagination} | |
components={components} | |
loading={loading} | |
onChange={handleTableChange} | |
rowKey={(record) => record.login.uuid} | |
> | |
<Column | |
width={'10vw'} | |
fixed="left" | |
title="name" | |
dataIndex="name" | |
key="name" | |
onFilter={(value, record) => record.name.indexOf(value) !== -1} | |
filters={[ | |
{ | |
text: 'Joe', | |
value: 'Joe', | |
}, | |
{ | |
text: 'Jim', | |
value: 'Jim', | |
}, | |
{ | |
text: 'Submenu', | |
value: 'Submenu', | |
children: [ | |
{ | |
text: 'Green', | |
value: 'Green', | |
}, | |
{ | |
text: 'Black', | |
value: 'Black', | |
}, | |
], | |
}, | |
]} | |
onCell={(record) => ({ | |
record, | |
editable: true, | |
dataIndex: 'name', | |
title: 'name', | |
handleSave: handleSave, | |
})} | |
render={(name) => `${name.first} ${name.last}`} | |
/> | |
<Column | |
width={'20vw'} | |
title="Email" | |
dataIndex="email" | |
key="email" | |
></Column> | |
<Column | |
width={'20vw'} | |
title="Gender" | |
dataIndex="gender" | |
key="gender" | |
filters={[ | |
{ text: 'Male', value: 'male' }, | |
{ text: 'Female', value: 'female' }, | |
]} | |
></Column> | |
<Column width={'20vw'} title="a" dataIndex="a" key="a"></Column> | |
<Column width={'20vw'} title="b" dataIndex="b" key="b"></Column> | |
<Column width={'20vw'} title="c" dataIndex="c" key="c"></Column> | |
<Column width={'20vw'} title="d" dataIndex="d" key="d"></Column> | |
<Column width={'20vw'} title="e" dataIndex="e" key="e"></Column> | |
<Column | |
title="Address" | |
dataIndex="address" | |
key="address" | |
width={'20vw'} | |
/> | |
<Column | |
width={'10vw'} | |
fixed="right" | |
title="Age" | |
dataIndex="age" | |
key="age" | |
sorter={(a, b) => a.age - b.age} | |
defaultSortOrder={'descend'} | |
filters={[ | |
{ text: '三十二', value: 32 }, | |
{ text: '四十二', value: 42 }, | |
]} | |
onFilter={(value, record) => record.age === value} | |
/> | |
<Column | |
width={'10vw'} | |
fixed="right" | |
title="operation" | |
dataIndex="operation" | |
render={(text, record) => | |
dataSource.length >= 1 ? ( | |
<Popconfirm | |
title="Sure to delete?" | |
onConfirm={() => handleDelete(record.key)} | |
> | |
<a>Delete</a> | |
</Popconfirm> | |
) : null | |
} | |
></Column> | |
</Table> | |
</div> | |
); | |
}; | |
export default ReportDemo; | |
//function component vs class component | |
// 1. 狀態的使用方法 | |
// => 使用state時候 不用使用this | |
// => 使用更改狀態的方法時 不用使用this | |
// => state改用useState 而不是用在constructor | |
// 2. 方法的呼叫 | |
// => 使用方法時, 不用this |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment