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')} />;
}, []);