pro-components icon indicating copy to clipboard operation
pro-components copied to clipboard

🧐[问题]我在ProFormField上使用了transform,但是onFinish拿到的还是原始值

Open elevenwangyang opened this issue 4 months ago • 4 comments

提问前先看看:

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>
  );
}

🚑 其他信息

elevenwangyang avatar Aug 19 '25 08:08 elevenwangyang

你遇到的问题是 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  Chat with Dosu Join Discord Share on X

dosubot[bot] avatar Aug 19 '25 08:08 dosubot[bot]

主要是文档稀碎,transform的用法基本靠猜测。。我测试出来有两种情况,一种是大家乐意看到的 transform={v=> fn(v)} 这种尝试不正确时,请尝试第二种 transform={v=> _.set({}, props.name, fn(v))}

asmodai1985 avatar Sep 03 '25 01:09 asmodai1985

再看看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> </> } `

asmodai1985 avatar Sep 03 '25 01:09 asmodai1985

如果数据是回填的 比如置了 initialValue transform只会起一次作用 后面就没用了 ,子元素设置了 transform会起作用,在proformlist上面设置的又不起做了

haobarry avatar Sep 12 '25 16:09 haobarry