bug复现:
input/textarea 设置了allowClear属性为true,当点击清除图标后,form中关联的input/textarea 中的值未更新。
import React from 'react';
import ReactDOM from 'react-dom';
import 'antd/dist/antd.css';
import './index.css';
import { Form, Input, Button } from 'antd';
const Demo = () => {
const [form] = Form.useForm()
const onFinish = (values) => {
console.log('Received values from form: ', values);
};
const onNumberChange = (e) => {
onChange(e.target.value);
};
return (
<Form
form={form}
name="customized_form_controls"
layout="inline"
onFinish={onFinish}
>
<Form.Item
name="name"
label="name"
>
<span>
<Input
allowClear="true"
type="text"
onChange={onNumberChange}
style={{
width: 100,
}}
/>
</span>
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form>
);
};
ReactDOM.render(<Demo />, document.getElementById('container'));
我们正常改变input/textarea 的值时,e.target.value的值和onFinish 时对应的值同步更新。当点击清除后,e.target.value值被清空。但是最终onFinish时获取到的值未更新。
原因分析:
主要是react合成事件带来的值更新不一致。
我们先来看下这个代码
import React, { useState } from "react";
import ReactDOM from "react-dom";
const Demo = () => {
const inputOnchange = (e) => {
console.log(1111111,e.target.value);
};
const spanOnchange = (e) => {
console.log(2222222,e.target.value);
};
const divOnchange = (e) => {
console.log(3333333,e.target.value);
};
return (
<div onChange={divOnchange}>
<span onChange={spanOnchange}>
<input onChange={inputOnchange} />
</span>
</div>
);
};
ReactDOM.render(<Demo />, document.getElementById("container"));
结果:
input/textarea 的onchange 事件是react合成事件 。
React合成事件一套机制:React并不是将click/change事件直接绑定在dom上面,而是采用事件冒泡的形式冒泡到document上面,然后React将事件封装给正式的函数处理运行和处理。
由此可见,由于react的合成机制,不管我们给input外层套上多少个dom元素,都可以捕获到值的变化并进行更新。这也就是为什么不管我们直接在Form组件中直接使用Input还是给Input外层套元素,我们在进行值的填充和删除时,Form均可以正确获得值的变化。
但是allowClear为什么不行呢?
我们来看一下antd源码关于这块的代码
从antd Input.tsx中可以看出,点击清除图标时执行了handleReset, handleReset中手动改变了一下当前input的值,并且调用resolveOnChange方法,该方法手动调用了一下Input上的onChange对应的方法,并没有触发事件冒泡机制,所以此时Input上的change方法执行,但是Input的父辈元素上的onChange方法并没有执行,导致获取的值没有更新。
解决方案:
手动触发父组件的onChange方法,实现事件的冒泡机制。这也是antd中对自定义form元素的解决方案。
import React from 'react';
import ReactDOM from 'react-dom';
import 'antd/dist/antd.css';
import './index.css';
import { Form, Input, Button } from 'antd';
const CustomInput = ({ onChange }) => {
const onNumberChange = (e) => {
onChange(e.target.value);
};
return (
<span>
<Input
allowClear="true"
type="text"
onChange={onNumberChange}
style={{
width: 100,
}}
/>
</span>
);
};
const Demo = () => {
const [form] = Form.useForm()
const onFinish = (values) => {
console.log('Received values from form: ', values);
};
return (
<Form
form={form}
name="customized_form_controls"
layout="inline"
onFinish={onFinish}
>
<Form.Item
name="name"
label="name"
>
<CustomInput onChange={(e) => {console.log(e)}}/>
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form>
);
};
ReactDOM.render(<Demo />, document.getElementById('container'));
希望可以帮到你们哦~~~