React.js:父组件传递给子组件的onSubmit函数未触发
解决方案:父子组件统一处理多API提交流程
问题分析
你当前的核心问题有两个:
- 子组件直接覆盖了props传入的
handleSubmit函数(函数组件props是只读的,这种写法本身不符合React规范) - 提交流程未联动:父组件只处理就诊创建,未在成功后触发子组件的队列提交逻辑
下面是具体修改方案,实现父组件统一触发提交,先完成就诊创建,再用返回的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;
关键修改点说明
- 禁止覆盖props函数:移除子组件中对
handleSubmit的重写,函数组件props为只读,直接修改会引发不可预期行为。 - 统一提交入口:父组件提交按钮作为唯一触发点,先完成就诊创建,再调用子组件暴露的
submitQueueEntry方法处理队列提交。 - 子组件状态自治:子组件自行管理
selectedQueueLocation等表单状态,提交时直接使用这些状态构建请求参数。 - Ref转发与方法暴露:通过
forwardRef和useImperativeHandle让父组件调用子组件内部方法,实现跨组件流程联动。 - 依赖项修正:父组件
handleSubmit添加middleName作为依赖,确保表单数据更新时回调能获取最新值。
内容的提问来源于stack exchange,提问作者CKW
相关产品推荐
相关产品推荐

