You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React+Ant Design:切换Tabs前提交表单的非命令式实现方案

问题

我用Ant Design的Tabs组件,每个Tab页嵌入一个Ant Design表单。需求是:切换Tab时,提示用户提交表单更改;若用户选择提交,需提交成功后再切换Tab;若选择不保存,则直接切换。

表单都是子组件,所以父组件需要通知子组件Tab即将切换,并触发表单提交。我已经用useImperativeHandle和forwardRef实现了这个功能,但想知道有没有非命令式的实现方式?

现有实现代码

表单组件(MyForm.tsx)

export type FormRef = { submit: (data?: Data) => Promise<boolean> };

export type Data = { someField: string };

const MyForm = (props: {}, ref: Ref<FormRef>) => {
  const [form] = useForm<Data>();
  useImperativeHandle(ref, () => ({ submit }));

  async function submit(data?: Data): Promise<boolean> {
    if (!data) data = form.getFieldsValue();
    const res = await submitData(data);
    return res;
  }

  return (
    <Form form={form} onFinish={submit}>
      <Form.Item name="someField">
        <Input />
      </Form.Item>
      <Form.Item>
        <Button htmlType="submit">Submit</Button>
      </Form.Item>
    </Form>
  );
};

export default forwardRef(MyForm);

父组件(App.tsx)

const App: FC = () => {
  const [activeKey, setActiveKey] = useState('tabOne');
  const formOneRef = useRef<FormRef>();
  const formTwoRef = useRef<FormRef>();

  async function onChange(key: string) {
    if (confirm('Save Changes?')) {
      if (activeKey === 'tabOne' && (await formOneRef.current.submit()))
        setActiveKey(key);
      if (activeKey === 'tabTwo' && (await formTwoRef.current.submit()))
        setActiveKey(key);
    } else setActiveKey(key);
  }

  const tabs = [
    {
      label: 'Tab One',
      key: 'tabOne',
      children: <MyForm ref={formOneRef} />,
    },
    {
      label: 'Tab Two',
      key: 'tabTwo',
      children: <MyForm ref={formTwoRef} />,
    },
  ];

  return <Tabs items={tabs} onChange={onChange} activeKey={activeKey} />;
};

提交函数(submitData.ts)

export default async function submitData(data: Data) {
  console.log('Submitting...', data);
  const res = await new Promise<boolean>((resolve) =>
    setTimeout(
      () => (Math.random() < 0.5 ? resolve(true) : resolve(false)),
      1000
    )
  );
  if (res) {
    console.log('Success!', data);
    return true;
  }
  if (!res) {
    console.error('Fake Error', data);
    return false;
  }
}

非命令式实现方案

核心思路是状态提升+回调传递,通过props建立父子组件的单向数据流通信,完全避免ref的命令式调用:

1. 改造表单组件

让表单组件接收onRegisterSubmitter回调,将自身的提交方法传递给父组件;同时保留内部的表单提交逻辑。

export type Data = { someField: string };

type MyFormProps = {
  onRegisterSubmitter: (submitter: () => Promise<boolean>) => void;
};

const MyForm = ({ onRegisterSubmitter }: MyFormProps) => {
  const [form] = useForm<Data>();

  async function submit(data?: Data): Promise<boolean> {
    if (!data) data = form.getFieldsValue();
    const res = await submitData(data);
    return res;
  }

  // 组件挂载时向父组件注册提交方法
  useEffect(() => {
    onRegisterSubmitter(submit);
  }, [onRegisterSubmitter, submit]);

  return (
    <Form form={form} onFinish={submit}>
      <Form.Item name="someField">
        <Input />
      </Form.Item>
      <Form.Item>
        <Button htmlType="submit">Submit</Button>
      </Form.Item>
    </Form>
  );
};

export default MyForm;

2. 改造父组件

父组件维护所有表单的提交方法映射,在Tab切换时调用当前激活Tab对应表单的提交逻辑,根据结果决定是否切换Tab:

const App: FC = () => {
  const [activeKey, setActiveKey] = useState('tabOne');
  // 存储每个Tab对应的表单提交方法
  const [submitters, setSubmitters] = useState<Record<string, () => Promise<boolean>>>({});

  // 注册单个表单的提交方法
  const registerSubmitter = (tabKey: string, submitter: () => Promise<boolean>) => {
    setSubmitters(prev => ({ ...prev, [tabKey]: submitter }));
  };

  async function onChange(key: string) {
    if (confirm('Save Changes?')) {
      const currentSubmitter = submitters[activeKey];
      if (currentSubmitter && (await currentSubmitter())) {
        setActiveKey(key);
      }
    } else {
      setActiveKey(key);
    }
  }

  const tabs = [
    {
      label: 'Tab One',
      key: 'tabOne',
      children: <MyForm onRegisterSubmitter={(submitter) => registerSubmitter('tabOne', submitter)} />,
    },
    {
      label: 'Tab Two',
      key: 'tabTwo',
      children: <MyForm onRegisterSubmitter={(submitter) => registerSubmitter('tabTwo', submitter)} />,
    },
  ];

  return <Tabs items={tabs} onChange={onChange} activeKey={activeKey} />;
};

方案优势

  • 完全遵循React单向数据流理念,组件间依赖关系清晰
  • 避免了ref命令式调用带来的耦合问题
  • 扩展性强,后续若需添加表单修改状态判断,只需在子组件中新增逻辑并通过props传递给父组件即可

内容的提问来源于stack exchange,提问作者Chris Hamilton

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 13:41:00