React - Input框绑定动态State和监听onChange事件,输入时失去焦点
- 一. 案例复现
- 二. 解决方案
一. 案例复现
案例代码如下:
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 (<><TablerowKey='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')} />;
}, []);