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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 20:55:10