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

React.js:父组件传递给子组件的onSubmit函数未触发

解决方案:父子组件统一处理多API提交流程

问题分析

你当前的核心问题有两个:

  1. 子组件直接覆盖了props传入的handleSubmit函数(函数组件props是只读的,这种写法本身不符合React规范)
  2. 提交流程未联动:父组件只处理就诊创建,未在成功后触发子组件的队列提交逻辑

下面是具体修改方案,实现父组件统一触发提交,先完成就诊创建,再用返回的visitUuid提交队列数据的完整流程。


父组件修改代码

import { useRef, useState } from 'react';

interface VisitFormProps {
  patientUuid: string;
  closePanel: () => void;
}
const StartVisitForm: React.FC<VisitFormProps> = ({ patientUuid, closePanel }) => {
  const [visitUuid, setVisitUuid] = useState<string | undefined>(undefined);
  const [middleName, setMiddleName] = useState('');
  const [ignoreChanges, setIgnoreChanges] = useState(false);
  // 用于获取子组件的提交方法
  const queueFieldsRef = useRef<{ submitQueueEntry: (visitUuid: string) => void }>(null);

  const handleSubmit = useCallback(
    (event) => {
      event.preventDefault();
      const payload: NewVisitPayload = {
        middleName // 补充父组件表单实际数据
      };
      const abortController = new AbortController();
      saveVisit(payload, abortController)
        .pipe(first())
        .subscribe(
          (response) => {
            if (response.status === 201) {
              setVisitUuid(response.data.uuid);
              showToast();
              // 就诊创建成功后,触发子组件的队列提交
              queueFieldsRef.current?.submitQueueEntry(response.data.uuid);
            }
          },
          (error) => { showNotification() },
        );
    },
    [middleName], // 添加依赖项,确保表单数据变化时更新回调
  );

  const patientState = useMemo(
    () => ({ patientUuid, visitUuid }),
    [patientUuid, visitUuid],
  );

  const handleOnChange = () => {
    setIgnoreChanges((prevState) => !prevState);
  };

  return (
    <Form className={styles.form} onChange={handleOnChange}>
      <div>
        <TextInput
          className={styles.input}
          id="middleName"
          labelText={t('middleName', 'Middle name')}
          onChange={(event) => setMiddleName(event.target.value)}
          value={middleName}
        />
        {/* 传递ref给子组件,若ExtensionSlot是自定义组件需实现ref转发 */}
        <ExtensionSlot 
          className={styles.content} 
          extensionSlotName="add-queue-entry-slot" 
          state={patientState}
          ref={queueFieldsRef}
        />
      </div>
      <Button onClick={handleSubmit} className={styles.button} kind="primary" type="submit">
        {t('startVisit', 'Start visit')}
      </Button>
    </Form>
  );
};
export default StartVisitForm;

子组件修改代码

import { useState, useImperativeHandle, forwardRef } from 'react';

// 使用forwardRef转发ref,让父组件能调用子组件方法
const StartVisitQueueFields = forwardRef<{ submitQueueEntry: (visitUuid: string) => void }, { patientUuid: string; visitUuid?: string }>(({ patientUuid, visitUuid }, ref) => {
  const [selectedQueueLocation, setSelectedQueueLocation] = useState('');

  // 暴露给父组件的队列提交方法
  useImperativeHandle(ref, () => ({
    submitQueueEntry: (visitUuid: string) => {
      if (!selectedQueueLocation) {
        showNotification('请选择队列位置');
        return;
      }
      const queuePayload: QueueEntryPayload = {
        patientUuid,
        visitUuid,
        location: selectedQueueLocation
        // 补充其他队列所需数据
      };
      const abortController = new AbortController();
      saveQueueEntry(queuePayload, abortController)
        .pipe(first())
        .subscribe(
          (response) => {
            if (response.status === 201) { 
              showToast('队列创建成功');
            }
          },
          (error) => { showNotification('队列创建失败') },
        );
    }
  }));

  return (
    <div className={styles.form}>
      <section className={styles.section}>
        <div className={styles.sectionTitle}>{t('queueLocation', 'Queue Location')}</div>
          <Layer>
            <Select
              labelText={t('selectQueueLocation', 'Select a queue location')}
              id="location"
              invalidText="Required"
              value={selectedQueueLocation}
              onChange={(event) => setSelectedQueueLocation(event.target.value)}>
              {/* 补充Select选项 */}
            </Select>
          </Layer>
      </section>
    </div>
  );
});

StartVisitQueueFields.displayName = 'StartVisitQueueFields';
export default StartVisitQueueFields;

关键修改点说明

  1. 禁止覆盖props函数:移除子组件中对handleSubmit的重写,函数组件props为只读,直接修改会引发不可预期行为。
  2. 统一提交入口:父组件提交按钮作为唯一触发点,先完成就诊创建,再调用子组件暴露的submitQueueEntry方法处理队列提交。
  3. 子组件状态自治:子组件自行管理selectedQueueLocation等表单状态,提交时直接使用这些状态构建请求参数。
  4. Ref转发与方法暴露:通过forwardRef和useImperativeHandle让父组件调用子组件内部方法,实现跨组件流程联动。
  5. 依赖项修正:父组件handleSubmit添加middleName作为依赖,确保表单数据更新时回调能获取最新值。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 19:09:31