React(TypeScript)中如何在父组件获取子组件状态并提交整合数据
实现父组件获取子组件表单数据并提交的方案
一、先修正Child1组件的错误
你的Child1组件存在几个影响数据收集的问题,先修正这些:
- 年龄输入框的
name属性错误写成了name,应改为age,否则输入年龄会覆盖姓名字段 - 状态更新逻辑错误:初始状态是
{ user: { name: "", age: "" } },但handlechange里误用了context,需改为user - 两个输入框的
id重复,需设置为唯一值
修正后的Child1组件(带TypeScript类型定义):
import { FormLabel, TextField } from "@mui/material"; import { useState } from "react"; // 定义用户数据类型 type UserData = { name: string; age: string; }; // 子组件接收父组件的回调与初始数据 type Child1Props = { onDataChange: (data: UserData) => void; initialData: UserData; }; function Child1({ onDataChange, initialData }: Child1Props) { const [client, setClient] = useState<UserData>(initialData); const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => { const { name, value } = event.target; const updatedClient = { ...client, [name]: value }; setClient(updatedClient); // 将更新后的数据同步给父组件 onDataChange(updatedClient); }; return ( <> <FormLabel>Name</FormLabel> <TextField name="name" id="name-input" value={client.name} onChange={handleChange} margin="normal" /> <FormLabel>Age</FormLabel> <TextField name="age" id="age-input" value={client.age} onChange={handleChange} margin="normal" type="number" /> </> ); } export default Child1;
二、父组件统一管理表单状态
将子组件的状态提升到父组件,通过回调函数接收子组件的表单数据,实现数据的统一收集:
import { Box, Tabs, Tab, TabPanel, Button } from "@mui/material"; import { useState } from "react"; import Child1 from "./Child1"; import Child2 from "./Child2"; // 定义整个表单的完整数据类型 type FormData = { user: { name: string; age: string; }; // 假设Child2收集地址信息,可根据实际需求调整 address: { street: string; city: string; }; }; function ParentComponent() { const [tabValue, setTabValue] = useState(0); // 初始化全局表单数据 const [formData, setFormData] = useState<FormData>({ user: { name: "", age: "" }, address: { street: "", city: "" } }); const handleTabChange = (event: React.SyntheticEvent, newValue: number) => { setTabValue(newValue); }; // 接收Child1的更新数据 const handleChild1Change = (userData: FormData["user"]) => { setFormData(prev => ({ ...prev, user: userData })); }; // 接收Child2的更新数据(逻辑与Child1一致) const handleChild2Change = (addressData: FormData["address"]) => { setFormData(prev => ({ ...prev, address: addressData })); }; // 提交表单到API的函数 const submitTabData = async () => { try { // 替换为你的实际API地址 const response = await fetch("/api/submit-form", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(formData) }); const result = await response.json(); console.log("提交成功", result); // 可添加提交成功后的逻辑,比如重置表单 } catch (error) { console.error("提交失败", error); } }; // 保持原有的无障碍属性函数 const a11yProps = (index: number) => { return { id: `simple-tab-${index}`, "aria-controls": `simple-tabpanel-${index}`, }; }; return ( <Box sx={{ width: "100%" }}> <Box sx={{ borderBottom: 1, borderColor: "divider" }}> <Tabs value={tabValue} onChange={handleTabChange} aria-label="basic tabs example" > <Tab label="Item One" {...a11yProps(0)} /> <Tab label="Item Two" {...a11yProps(1)} /> <Tab label="Item Three" {...a11yProps(2)} /> </Tabs> </Box> <TabPanel value={tabValue} index={0}> <Child1 initialData={formData.user} onDataChange={handleChild1Change} /> </TabPanel> <TabPanel value={tabValue} index={1}> <Child2 initialData={formData.address} onDataChange={handleChild2Change} /> </TabPanel> <TabPanel value={tabValue} index={2}> Item Three </TabPanel> <Box sx={{ mt: 2 }}> <Button onClick={submitTabData} variant="contained">Submit</Button> </Box> </Box> ); } export default ParentComponent;
三、Child2组件示例(参考Child1实现)
import { FormLabel, TextField } from "@mui/material"; import { useState } from "react"; type AddressData = { street: string; city: string; }; type Child2Props = { onDataChange: (data: AddressData) => void; initialData: AddressData; }; function Child2({ onDataChange, initialData }: Child2Props) { const [address, setAddress] = useState<AddressData>(initialData); const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => { const { name, value } = event.target; const updatedAddress = { ...address, [name]: value }; setAddress(updatedAddress); onDataChange(updatedAddress); }; return ( <> <FormLabel>Street</FormLabel> <TextField name="street" id="street-input" value={address.street} onChange={handleChange} margin="normal" /> <FormLabel>City</FormLabel> <TextField name="city" id="city-input" value={address.city} onChange={handleChange} margin="normal" /> </> ); } export default Child2;
核心逻辑说明
- 状态提升:将子组件的表单状态移到父组件,实现全局数据统一管理
- 回调传值:子组件通过props接收父组件的回调函数,每次输入变化时同步最新数据到父组件
- 类型安全:用TypeScript定义明确的数据类型,避免类型错误
点击Submit按钮时,父组件的formData已包含所有子组件的表单数据,直接转换为JSON格式提交到API即可。
内容的提问来源于stack exchange,提问作者Sandeep Thomas
相关产品推荐
相关产品推荐

