一. 案例复现
案例代码如下:
import React, { useState } from 'react';
import { Table, Input } from 'antd';
const Column = Table.Column;
const mockData = [{
id: 1,
name: 'test1',
address: 'address1',
}, {
id: 2,
name: 'test1',
address: 'address2',
}];
const index = () => {
const [ data, setData ] = useState(mockData);
const handleChange = (event: any, record: any, name: string) => {
const currentVal = event.target.value;
const { id } = record;
const newData = data.map((item: any) => {
return {
...item,
[name]: item.id === id ? currentVal : item[name],
};
});
setData(newData);
};
const AddressColumn = (props: any) => {
const { record } = props;
return <Input value={record.address} onChange={event => handleChange(event, record, 'address')} />;
};
return (
<>
<Table
rowKey='id'
style={{ width: '100%' }}
dataSource={data}>
<Column title='ID' key='id' dataIndex='id' />
<Column title={'name'} key='name' dataIndex='name' render={(val: string, record: any) => {
return <Input value={val} onChange={event => handleChange(event, record, 'name')} />;
}} />
<Column title={'address'} key='address' dataIndex='address' render={(val: string, record: any) => <AddressColumn record={record} />} />
</Table>
</>
);
};
export default index;
页面如下
简单画了一个表格:
name
列可以修改,并且会动态修改对应的state
。address
列同理,只不过用的是子组件AddressColumn
渲染。
分别尝试在name
列以及address
列中更改Input
框的内容,效果如下:
仔细观察可以发现:
name
列中的文本,可以随意输入,没有任何的限制。address
列中的Input
框内容一旦更改,就会失去焦点。
原因如下:
- 每次修改address列中的属性,动态修改了state的值。
- 组件重新渲染,导致AddressColumn组件也重新渲染。因此会生成一个新的
Input
框,导致失焦。
二. 解决方案
方式一:就如上述案例一样,采用name
列式的写法,即将子组件的内容,提升到父组件中:
<Column title={'address'} key='address' dataIndex='address' render={(val: string, record: any) => <AddressColumn record={record} />} />
改为
<Column title={'address'} key='address' dataIndex='address' render={(val: string, record: any) => {
return <Input value={val} onChange={event => handleChange(event, record, 'address')} />;
}} />
方式二:使用useMemo
钩子,封装一下子组件AddressColumn
避免重复渲染。
const AddressColumn = (props: any) => {
const { record } = props;
return <Input value={record.address} onChange={event => handleChange(event, record, 'address')} />;
};
改为
const AddressColumn = useMemo(() => (props: any) => {
const { record } = props;
return <Input value={record.address} onChange={event => handleChange(event, record, 'address')} />;
}, []);