3

Consider the following code:

import { Button, Form } from 'antd'

export function App() {
  const submitForm = (data: {submit: 'foo' | 'bar'}) => {
    console.log(data)
  }

  return (
    <Form onFinish={submitForm}>
      <Button htmlType="submit" name="submit" value="foo">Foo</Button>
      <Button htmlType="submit" name="submit" value="bar">Bar</Button>
    </Form>
  )
}

When clicking either button, I would expect data.submit in submitForm to be either foo or bar, but it is unset entirely; console.log returns {} instead. The ability to provide HTML buttons name and value is standard, and allows for forms where there are multiple submit buttons (like different actions), but for the same input data. Is there a way to obtain a similar behaviour with antd's Form?

I've seen it suggested to use useRef instead and capture it for each button's onClick event; but I was really hoping there would be a nicer solution.

1
  • Just in case someone wonders about the useRef solution: have a ref which you check for in the submitForm (i.e. ref.current) and in onClick do ref.current = "foo";. Commented Oct 21 at 9:27

2 Answers 2

2

I don't think you'll find any much more "elegant" solution to the useRef solution if you are set on using AntD forms and components.

The problem is that you have no access to the underlying form element's onSubmit event object in submitForm and your form has no fields, and therefore no values to submit and pass to the submitForm/onFinish handler, which is why you see only an empty {} object logged.

An (IMHO) as-trivial alternative I see is using a hidden "submit" field and the form instance directly to set its value.

Example:

export function MyForm() {
  // Create a form instance manually
  const [form] = Form.useForm();

  const submitForm = (data: { submit: "foo" | "bar" }) => {
    console.log(data);
  };

  return (
    <Form
      form={form} // pass form instance
      onFinish={submitForm}
    >
      {/* Hidden "submit" field */}
      <Form.Item name="submit" hidden></Form.Item>
      <Button
        htmlType="submit"
        onClick={() => {
          // Manually set "submit" field value to "foo"
          form.setFieldValue("submit", "foo");
        }}
      >
        Foo
      </Button>
      <Button
        htmlType="submit"
        onClick={() => {
          // Manually set "submit" field value to "boo"
          form.setFieldValue("submit", "bar");
        }}
      >
        Bar
      </Button>
    </Form>
  );
}

Another alternative I can think of is to assign an ID to the form and access the underlying form DOM element and add your own "submit" event listener and do whatever you need to do with the submit event object.

Note however, that it generally is not advised to access DOMNodes directly in React if other means are available. In other words, this would be more of a "all other methods have been exhausted" type of solution.

Example:

useEffect(() => {
  const formEl = document.getElementById("myForm");

  const submitHandler = (e: SubmitEvent) => {
    // Do what you need with `e.submitter?.value`, the 
    // button element's `value` prop value.
  };

  if (formEl) {
    formEl.addEventListener("submit", submitHandler);

    return () => {
      formEl.removeEventListener("submit", submitHandler);
    };
  }
}, []);

...

return (
  <Form name="myForm" onFinish={submitForm}>
    <Button htmlType="submit" name="submit" value="foo">Foo</Button>
    <Button htmlType="submit" name="submit" value="bar">Bar</Button>
  </Form>
)

For example, you could save the value into a React Ref or state, or issue any other additional side-effect logic, though this isn't necessarily better than the useRef solution or the hidden field suggestions.

Sign up to request clarification or add additional context in comments.

Comments

0

Drew Reese Answer is correct, This is my approach using validateFields method

Demo

import React from 'react';

import { Button, Form, Input, Select, Space } from 'antd';

const { Option } = Select;

const App: React.FC = () => {
  const [form] = Form.useForm();

  const onGenderChange = (value: string) => {
    switch (value) {
      case 'male':
        form.setFieldsValue({ note: 'Hi, man!' });
        break;
      case 'female':
        form.setFieldsValue({ note: 'Hi, lady!' });
        break;
      case 'other':
        form.setFieldsValue({ note: 'Hi there!' });
        break;
      default:
    }
  };

  const onFinish = (data: string) => {
    form.validateFields().then((values) => {
      alert(data + ' ' + JSON.stringify(values));
    });
  };

  const onReset = () => {
    form.resetFields();
  };

  const onFill = () => {
    form.setFieldsValue({ note: 'Hello world!', gender: 'male' });
  };

  return (
    <Form form={form} name="control-hooks" style={{ maxWidth: 600 }}>
      <Form.Item name="note" label="Note" rules={[{ required: true }]}>
        <Input />
      </Form.Item>
      <Form.Item name="gender" label="Gender" rules={[{ required: true }]}>
        <Select
          placeholder="Select a option and change input text above"
          onChange={onGenderChange}
          allowClear
        >
          <Option value="male">male</Option>
          <Option value="female">female</Option>
          <Option value="other">other</Option>
        </Select>
      </Form.Item>
      <Form.Item
        noStyle
        shouldUpdate={(prevValues, currentValues) =>
          prevValues.gender !== currentValues.gender
        }
      >
        {({ getFieldValue }) =>
          getFieldValue('gender') === 'other' ? (
            <Form.Item
              name="customizeGender"
              label="Customize Gender"
              rules={[{ required: true }]}
            >
              <Input />
            </Form.Item>
          ) : null
        }
      </Form.Item>
      <Form.Item>
        <Space>
          <Button type="primary" onClick={() => onFinish('one')}>
            Submit 1
          </Button>
          <Button type="primary" onClick={() => onFinish('two')}>
            Submit 2
          </Button>
          <Button type="primary" onClick={() => onFinish('three')}>
            Submit 3
          </Button>
          <Button htmlType="button" onClick={onReset}>
            Reset
          </Button>
          <Button type="link" htmlType="button" onClick={onFill}>
            Fill form
          </Button>
        </Space>
      </Form.Item>
    </Form>
  );
};

export default App;

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.