React:表单提交按钮在父组件,能否将onSubmit事件传给子组件?
React表单提交时传递事件给子组件的实现方案
你要的需求完全可以实现,下面是几种适合你场景的常用方案:
方案一:状态提升+父组件统一收集数据(推荐)
这是React表单的常规做法,把每个Card的选中状态移到父组件管理,提交时直接从父组件获取所有数据,同时也能通过回调通知子组件。
修改后的父组件代码
// Parent Component import React, { useState } from "react"; import Card from "./Card"; function ParentComponent({ cardsList }) { // 存储所有题目的选中状态,key为题目内容,value为选中的选项ID const [selectedAnswers, setSelectedAnswers] = useState({}); // 接收子组件传来的选中状态更新 const handleAnswerChange = (question, selectedId) => { setSelectedAnswers(prev => ({ ...prev, [question]: selectedId })); }; function handleSubmit(event) { event.preventDefault(); // 在这里处理所有选中的答案,比如提交到后端 console.log("所有选中的答案:", selectedAnswers); } const card = cardsList.map(el => { const question = decodeHtml(el.question); return ( <div className="card-holder" key={question}> <Card question={question} answer={el.correct_answer} incorrectAnswers={el.incorrect_answers} selectedId={selectedAnswers[question] || false} onAnswerChange={(selectedId) => handleAnswerChange(question, selectedId)} /> </div> ) }) return ( <form className="game" onSubmit={handleSubmit}> {card} <input type="submit" value="Submit" className="submit-btn" /> </form> ) }
修改后的子组件代码
// Child Component import React, { useState } from "react" import {shuffle, decodeHtml} from "/src/script/Helper.js" function Card(props) { const [arr, setArr] = React.useState(shuffle([0,1,2,3])); // 点击选项时通知父组件更新状态 function toggleCheck(event) { props.onAnswerChange(event.target.id); } return ( <div className="card"> <h4 className="card--question">{props.question}</h4> <div className="card--answers"> <div className="card--answer"> <input type="radio" name={props.question} id={`${props.question} ans1`} onChange={toggleCheck} checked={props.selectedId === `${props.question} ans1`} correct={arr[0] === 3 ? "true" : "false"} /> <label htmlFor={`${props.question} ans1` }>{arr[0] === 3 ? decodeHtml(props.answer) : decodeHtml(props.incorrectAnswers[arr[0]])}</label> </div> <div className="card--answer"> <input type="radio" name={props.question} id={`${props.question} ans2`} onChange={toggleCheck} checked={props.selectedId === `${props.question} ans2`} correct={arr[1] === 3 ? "true" : "false"} /> <label htmlFor={`${props.question} ans2`}>{arr[1] === 3 ? decodeHtml(props.answer) : decodeHtml(props.incorrectAnswers[arr[1]])}</label> </div> <div className="card--answer"> <input type="radio" name={props.question} id={`${props.question} ans3`} onChange={toggleCheck} checked={props.selectedId === `${props.question} ans3`} correct={arr[2] === 3 ? "true" : "false"} /> <label htmlFor={`${props.question} ans3`}>{arr[2] === 3 ? decodeHtml(props.answer) : decodeHtml(props.incorrectAnswers[arr[2]])}</label> </div> <div className="card--answer"> <input type="radio" name={props.question} id={`${props.question} ans4`} onChange={toggleCheck} checked={props.selectedId === `${props.question} ans4`} correct={arr[3] === 3 ? "true" : "false"} /> <label htmlFor={`${props.question} ans4`}>{arr[3] === 3 ? decodeHtml(props.answer) : decodeHtml(props.incorrectAnswers[arr[3]])}</label> </div> </div> </div> ) } export default Card;
方案二:通过Ref调用子组件的提交方法
如果需要让每个子组件在提交时执行自己的逻辑(比如验证是否选中),可以用useRef获取子组件实例,然后在父组件提交时调用子组件的方法。
修改后的父组件代码
// Parent Component import React, { useRef } from "react"; import Card from "./Card"; function ParentComponent({ cardsList }) { // 存储所有Card组件的Ref const cardRefs = useRef([]); function handleSubmit(event) { event.preventDefault(); // 遍历所有子组件,调用它们的提交处理方法 cardRefs.current.forEach(ref => { if (ref) { ref.handleSubmit(); } }); // 收集所有子组件的选中答案 const allAnswers = cardRefs.current .map(ref => ref?.getSelectedAnswer()) .filter(answer => answer); console.log("所有选中的答案:", allAnswers); } const card = cardsList.map((el, index) => { const question = decodeHtml(el.question); return ( <div className="card-holder" key={question}> <Card ref={el => cardRefs.current[index] = el} question={question} answer={el.correct_answer} incorrectAnswers={el.incorrect_answers} /> </div> ) }) return ( <form className="game" onSubmit={handleSubmit}> {card} <input type="submit" value="Submit" className="submit-btn" /> </form> ) }
修改后的子组件代码
// Child Component import React, { useState, forwardRef, useImperativeHandle } from "react" import {shuffle, decodeHtml} from "/src/script/Helper.js" // 用forwardRef包裹组件,允许父组件获取Ref const Card = forwardRef((props, ref) => { const [arr, setArr] = React.useState(shuffle([0,1,2,3])); const [checked, setChecked] = React.useState(false); function toggleCheck(event) { setChecked(event.target.id); } // 子组件自身的提交处理逻辑 const handleSubmit = () => { if (!checked) { console.log(`问题"${props.question}"未选择答案`); return; } console.log(`问题"${props.question}"的选中答案:`, checked); }; // 暴露方法给父组件调用 useImperativeHandle(ref, () => ({ handleSubmit, // 返回当前选中的答案信息 getSelectedAnswer: () => ({ question: props.question, selectedId: checked, isCorrect: checked.includes("ans") && arr[parseInt(checked.split("ans")[1])-1] === 3 }) })); return ( <div className="card"> <h4 className="card--question">{props.question}</h4> <div className="card--answers"> <div className="card--answer"> <input type="radio" name={props.question} id={`${props.question} ans1`} onChange={toggleCheck} checked={checked === `${props.question} ans1`} correct={arr[0] === 3 ? "true" : "false"} /> <label htmlFor={`${props.question} ans1` }>{arr[0] === 3 ? decodeHtml(props.answer) : decodeHtml(props.incorrectAnswers[arr[0]])}</label> </div> <div className="card--answer"> <input type="radio" name={props.question} id={`${props.question} ans2`} onChange={toggleCheck} checked={checked === `${props.question} ans2`} correct={arr[1] === 3 ? "true" : "false"} /> <label htmlFor={`${props.question} ans2`}>{arr[1] === 3 ? decodeHtml(props.answer) : decodeHtml(props.incorrectAnswers[arr[1]])}</label> </div> <div className="card--answer"> <input type="radio" name={props.question} id={`${props.question} ans3`} onChange={toggleCheck} checked={checked === `${props.question} ans3`} correct={arr[2] === 3 ? "true" : "false"} /> <label htmlFor={`${props.question} ans3`}>{arr[2] === 3 ? decodeHtml(props.answer) : decodeHtml(props.incorrectAnswers[arr[2]])}</label> </div> <div className="card--answer"> <input type="radio" name={props.question} id={`${props.question} ans4`} onChange={toggleCheck} checked={checked === `${props.question} ans4`} correct={arr[3] === 3 ? "true" : "false"} /> <label htmlFor={`${props.question} ans4`}>{arr[3] === 3 ? decodeHtml(props.answer) : decodeHtml(props.incorrectAnswers[arr[3]])}</label> </div> </div> </div> ) }); export default Card;
方案三:使用Context传递提交状态
如果组件层级较深,或者多个组件需要响应提交事件,可以用Context来统一管理提交状态。
第一步:创建SubmitContext
// SubmitContext.js import React, { createContext, useContext, useState } from "react"; const SubmitContext = createContext(); export function SubmitProvider({ children }) { const [isSubmitting, setIsSubmitting] = useState(false); // 触发提交逻辑的方法 const triggerSubmit = () => { setIsSubmitting(true); setTimeout(() => setIsSubmitting(false), 0); // 确保状态更新触发子组件Effect }; return ( <SubmitContext.Provider value={{ isSubmitting, triggerSubmit }}> {children} </SubmitContext.Provider> ); } // 自定义Hook,方便子组件使用Context export function useSubmit() { return useContext(SubmitContext); }
修改后的父组件代码
// Parent Component import React from "react"; import Card from "./Card"; import { SubmitProvider, useSubmit } from "./SubmitContext"; function ParentComponent({ cardsList }) { const { triggerSubmit } = useSubmit(); function handleSubmit(event) { event.preventDefault(); // 触发Context中的提交状态更新 triggerSubmit(); console.log("表单提交"); } const card = cardsList.map(el => { const question = decodeHtml(el.question); return ( <div className="card-holder" key={question}> <Card question={question} answer={el.correct_answer} incorrectAnswers={el.incorrect_answers} /> </div> ) }) return ( <SubmitProvider> <form className="game" onSubmit={handleSubmit}> {card} <input type="submit" value="Submit" className="submit-btn" /> </form> </SubmitProvider> ) }
修改后的子组件代码
// Child Component import React, { useState, useEffect } from "react" import {shuffle, decodeHtml} from "/src/script/Helper.js" import { useSubmit } from "./SubmitContext"; function Card(props) { const [arr, setArr] = React.useState(shuffle([0,1,2,3])); const [checked, setChecked] = React.useState(false); const { isSubmitting } = useSubmit(); // 监听提交状态变化,执行子组件逻辑 useEffect(() => { if (isSubmitting) { handleSubmit(); } }, [isSubmitting]); function toggleCheck(event) { setChecked(event.target.id); } const handleSubmit = () => { if (!checked) { console.log(`问题"${props.question}"未选择答案`); return; } console.log(`问题"${props.question}"的选中答案:`, checked); }; return ( <div className="card"> <h4 className="card--question">{props.question}</h4> <div className="card--answers"> <div className="card--answer"> <input type="radio" name={props.question} id={`${props.question} ans1`} onChange={toggleCheck} checked={checked === `${props.question} ans1`} correct={arr[0] === 3 ? "true" : "false"} /> <label htmlFor={`${props.question} ans1` }>{arr[0] === 3 ? decodeHtml(props.answer) : decodeHtml(props.incorrectAnswers[arr[0]])}</label> </div> <div className="card--answer"> <input type="radio" name={props.question} id={`${props.question} ans2`} onChange={toggleCheck} checked={checked === `${props.question} ans2`} correct={arr[1] === 3 ? "true" : "false"} /> <label htmlFor={`${props.question} ans2`}>{arr[1] === 3 ? decodeHtml(props.answer) : decodeHtml(props.incorrectAnswers[arr[1]])}</label> </div> <div className="card--answer"> <input type="radio" name={props.question} id={`${props.question} ans3`} onChange={toggleCheck} checked={checked === `${props.question} ans3`} correct={arr[2] === 3 ? "true" : "false"} /> <label htmlFor={`${props.question} ans3`}>{arr[2] === 3 ? decodeHtml(props.answer) : decodeHtml(props.incorrectAnswers[arr[2]])}</label> </div> <div className="card--answer"> <input type="radio" name={props.question} id={`${props.question} ans4`} onChange={toggleCheck} checked={checked === `${props.question} ans4`} correct={arr[3] === 3 ? "true" : "false"} /> <label htmlFor={`${props.question} ans4`}>{arr[3] === 3 ? decodeHtml(props.answer) : decodeHtml(props.incorrectAnswers[arr[3]])}</label> </div> </div> </div> ) } export default Card;
内容的提问来源于stack exchange,提问作者tan shi yu
相关产品推荐
相关产品推荐

