Antd 中Modal和Form配合时验证器无效和默认值不更新问题
验证器无效问题
发现过程
在跟着B站React实战课程走的时候,因为课程是Antd V3我使用的是V4。所以在遇到Modal中嵌套From时,课程使用的是高阶组件的方式实现验证器。我看见V4直接有rules(包括使用validator)属性,直接设置后发现无效。
解决方法
其实在官方文档有Modal组件配合的实例。
import React, { useState } from 'react';
import { Button, Modal, Form, Input, Radio } from 'antd';
interface Values {
title: string;
description: string;
modifier: string;
}
interface CollectionCreateFormProps {
visible: boolean;
onCreate: (values: Values) => void;
onCancel: () => void;
}
const CollectionCreateForm: React.FC<CollectionCreateFormProps> = ({
visible,
onCreate,
onCancel,
}) => {
const [form] = Form.useForm();
return (
<Modal
visible={visible}
title="Create a new collection"
okText="Create"
cancelText="Cancel"
onCancel={onCancel}
onOk={() => {
form
.validateFields()
.then(values => {
form.resetFields();
onCreate(values);
})
.catch(info => {
console.log('Validate Failed:', info);
});
}}
>
<Form
form={form}
layout="vertical"
name="form_in_modal"
initialValues={{ modifier: 'public' }}
>
<Form.Item
name="title"
label="Title"
rules={[{ required: true, message: 'Please input the title of collection!' }]}
>
<Input />
</Form.Item>
<Form.Item name="description" label="Description">
<Input type="textarea" />
</Form.Item>
<Form.Item name="modifier" className="collection-create-form_last-form-item">
<Radio.Group>
<Radio value="public">Public</Radio>
<Radio value="private">Private</Radio>
</Radio.Group>
</Form.Item>
</Form>
</Modal>
);
};
const CollectionsPage = () => {
const [visible, setVisible] = useState(false);
const onCreate = values => {
console.log('Received values of form: ', values);
setVisible(false);
};
return (
<div>
<Button
type="primary"
onClick={() => {
setVisible(true);
}}
>
New Collection
</Button>
<CollectionCreateForm
visible={visible}
onCreate={onCreate}
onCancel={() => {
setVisible(false);
}}
/>
</div>
);
};
ReactDOM.render(<CollectionsPage />, mountNode);
.collection-create-form_last-form-item {
margin-bottom: 0;
}
CollectionCreateForm 时最关键的部分,这里使用里React的hook语法。我通过一些更改把它还是变成了封装一个类(感觉这样也不好)。
代码如下:
import { Form, Modal } from 'antd';
import React, { Component } from 'react';
import { ICategory } from './DataModel';
import { ModalStatusCode } from './ModalStatusCode';
interface Values {
title: string;
description: string;
modifier: string;
}
interface ModalFromProps {
showStatus: ModalStatusCode;
onCancel: () => void;
category: ICategory;
element: React.ReactNode;
expectedStatus: ModalStatusCode;
title: string;
}
interface ModalState {
name: string;
}
class ModalForm extends Component<ModalFromProps, ModalState> {
constructor(props: ModalFromProps) {
super(props);
this.state = {
name: '',
};
}
private onCancel = () => {
this.props.onCancel();
};
private CreateModalFrom = (): any => {
const [form] = Form.useForm();
const { title } = this.props;
return (
<Modal
visible={this.props.showStatus === this.props.expectedStatus}
title={title}
okText="Ok"
cancelText="Cancel"
onCancel={this.onCancel}
onOk={() => {
form
.validateFields()
.then((values) => {
form.resetFields();
console.log(values);
})
.catch((info) => {
console.log('Validate Failed:', info);
});
}}
>
<Form form={form}>
<Form.Item name="title" rules={[{ required: true, message: '请输入品类名称' }]} initialValue={this.props.category.name} >
{this.props.element}
</Form.Item>
</Form>
</Modal>
);
};
render() {
return <this.CreateModalFrom />;
}
}
因为是React新手,所以还是凭着自己理解去封装。这样导致自定义ModalForm组件为了得到某些值必须经过某个中间组件去传,看起来就很难看且值没有任何更改,很难看。希望各位也能给个好建议。
调用ModalForm代码:
interface IUpdateFormProps {
category: ICategory;
onCancel: () => void;
showStatus: ModalStatusCode;
}
interface IUpdateFormState {
}
export default class UpdateFrom extends Component<IUpdateFormProps, IUpdateFormState> {
render() {
return (
<ModalForm {...this.props} element={<Input placeholder="请输入分类名称"></Input>} expectedStatus={ModalStatusCode.Update} title='更新分类'></ModalForm>
);
}
}
对应页面上有一个列表,列表有一个修改功能。点击之后Form的input框里面显示该行属性名称。但是奇怪的是这样写名称会不更新。始终是第一个点击那一行,尝试后发现点击OK按钮名称会更新。查看代码后发现是
form.resetFields();
起的作用,因此想着每次都调用这个方法,但尝试一番无果。晚上再去看Modal的官方文档后发现这么一句话。
<Modal /> 默认关闭后状态不会自动清空, 如果希望每次打开都是新内容,请设置 destroyOnClose。
<Modal /> 和 Form 一起配合使用时,设置 destroyOnClose 也不会在 Modal 关闭时销毁表单字段数据,
需要设置 <Form preserve={false} />。
于是想着能不能每次打开都是重新渲染,解决这个问题。于是在添加上面的属性后便解决了问题。
思考
虽然解决了问题但是感觉有更好解决方案或者另一种实现方法,如果能还希望不吝赐教。
随笔
从偶然的出生,到必然的死亡,我们都将经历孤独和徘徊。