🧐[问题]我在ProFormField上使用了transform,但是onFinish拿到的还是原始值
提问前先看看:
https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way/blob/main/README-zh_CN.md
🧐 问题描述
我希望提交到后端的数据是一个 json 字符串,在组件中需要 object 类型的值,我尝试使用了官方提供的convertValue和transform组件来做这个事,但是我发现transform虽然会成功转化了数据,但是在onFinish提交的时,拿到的 values 内部还是一个 object 类型的值,这是为什么呢?
💻 示例代码
import { ProForm, ProFormField } from '@ant-design/pro-components';
import { Input, Select, type SelectProps, Space, Typography } from 'antd';
interface ITypeValue {
type?: string;
value?: string;
}
interface SelectInputProps {
value?: ITypeValue;
onChange?: (value?: ITypeValue) => void;
options?: SelectProps['options'];
}
const SelectInput = (props: SelectInputProps) => {
const { value = {}, onChange } = props;
const handleChange = (key: 'type' | 'value', val?: string) => {
const newValue = { ...value, [key]: val };
onChange?.(newValue.type || newValue.value ? newValue : undefined);
};
return (
<Space.Compact style={{ width: '100%' }}>
<Select
style={{ width: 100 }}
placeholder="请选择"
value={value?.type}
options={props.options}
onChange={type => handleChange('type', type || undefined)}
allowClear
/>
<Input
placeholder="请输入"
defaultValue={value?.value}
onBlur={e => handleChange('value', e.target.value)}
allowClear
/>
</Space.Compact>
);
};
export default function () {
return (
<ProForm
onFinish={async values => {
console.log(values);
}}
>
<ProFormField
name="value"
label="自定义下拉输入组件"
transform={values => {
const res =
typeof values === 'object' ? JSON.stringify(values) : values;
console.log('transform', res);
return res;
}}
convertValue={values => {
const res = typeof values === 'string' ? JSON.parse(values) : values;
console.log('convertValue', res);
return res;
}}
renderFormItem={() => (
<SelectInput options={[{ label: 'label', value: 'value' }]} />
)}
/>
</ProForm>
);
}
🚑 其他信息
你遇到的问题是 ProFormField 的 transform 没有生效,onFinish 里拿到的还是原始值。这其实是 ProForm 的已知限制,尤其是在自定义 renderFormItem(比如你用的 SelectInput)时,transform 很可能不会被触发,导致 onFinish 拿到的不是 transform 处理后的值,而是原始值。这是因为自定义渲染会绕过 ProFormField 的内部值处理逻辑,transform 只有在表单值经过 ProFormField 的标准管道时才会生效。
官方文档说明 transform 应该在提交时转换表单值,但社区有大量反馈,像自定义组件、ProFormList、initialValue 等场景下 transform 经常失效,维护者建议用 onValuesChange 或在 onFinish 里手动处理数据作为 workaround(比如在 onFinish 里自己 JSON.stringify)参考1 参考2 参考3。
如果你希望 transform 生效,可以尝试不用 renderFormItem,直接用 ProForm 提供的内置组件。如果必须自定义渲染,建议在 onFinish 里手动处理 value 字段,比如:
onFinish={async values => {
values.value = typeof values.value === 'object' ? JSON.stringify(values.value) : values.value;
// 其他处理
}}
更多细节和类似问题可以参考官方 transform 文档和相关 issue。
To reply, just mention @dosu.
How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other
主要是文档稀碎,transform的用法基本靠猜测。。我测试出来有两种情况,一种是大家乐意看到的 transform={v=> fn(v)} 这种尝试不正确时,请尝试第二种 transform={v=> _.set({}, props.name, fn(v))}
再看看transform的测试用例 https://github.com/ant-design/pro-components/blob/master/tests/form/transform.test.tsx 这里面的例子自己拿来试试,基本都过时了。。我拿了例子按照我的思路修改下提供参考 ` import { ProForm, ProFormDatePicker, ProFormList, ProFormText, } from '@ant-design/pro-components'; export default () => { const fn = (v)=> console.log(JSON.stringify(v, null, 2)) return <>
<ProForm onFinish={async (values) => { fn(values); }} > <ProFormList name="departments"> <ProFormText name="name111" transform={(value) => { return { name111: value+':1111' }; }} /> <ProFormList name="employees"> <ProFormText name="name222" transform={(value) => { return { name222: value+':2222' }; }} /> </ProFormList> </ProFormList> </ProForm> </> } `
如果数据是回填的 比如置了 initialValue transform只会起一次作用 后面就没用了 ,子元素设置了 transform会起作用,在proformlist上面设置的又不起做了