[英]Get values from nested forms using final form in react/typescript
我無法使用最終形式從嵌套的 forms 中獲取值。
這是我的index.tsx
import React, { useState, ChangeEvent } from 'react';
import { Form } from 'react-final-form';
import {
Box, Typography, Button, IconButton, Grid, FormControl, InputLabel, Select,
} from '@material-ui/core';
import AddCircleIcon from '@material-ui/icons/AddCircle';
import FieldInput from 'forms/shared/fields/input';
import FieldFileUploader from 'forms/shared/fields/file-uploader';
import FieldCheckbox from 'forms/shared/fields/checkbox';
import FormRadioQuestions from './partials/form-radio';
import FormExperience from './partials/form-experience';
import FormEducation from './partials/form-education';
import FormPersonalInfo from './partials/form-personal-info';
import FormGovernmentIds from './partials/form-government-id';
import ItemExperience from './partials/experience-item';
import ItemEducation from './partials/education-item';
import useStyles from './styles';
const PublicApplicationForm = () => {
const [state, setState] = useState<{ client: string | number; name: string }>({ client: '', name: 'hai' });
const [showExp, setOpenExp] = useState(false);
const [showEdu, setOpenEdu] = useState(false);
const [data, setData] = useState({
experience: [{}],
});
const [educations, setEducations] = useState([]);
const [experiences, setExperiences] = useState([]);
const classes = useStyles();
const radioValues = [{ value: 'yes', label: 'Yes' }, { value: 'no', label: 'No' }];
const variables = { title: 'How did you hear about us?*', typeOfService: 'Type of Service*' };
const relationOptions = ['Walk-In', 'Employee Referral', 'Job Boards', 'Job Fair', 'Social Media'];
const checkBoxLabel = 'Please be informed that your application to this job offer will trigger some processing of your personal data by the company. For more information on data processing, please refer to the company’s talent acquisition privacy policy.';
const handleChange = ({ target }: ChangeEvent<{ name?: string; value: unknown }>) => {
setState({ ...state, [target.name as keyof typeof state]: target.value });
};
const handleBlur = (event: any) => {
data.experience[0] = { ...data.experience[0], [event.target.name]: event.target.value };
setData({ ...data });
};
const onAddEdu = (edu: any) => { setEducations([...educations, edu]); setOpenEdu(false); };
const onAddExp = (exp: any) => { setExperiences([...experiences, exp]); setOpenExp(false); };
return (
<Grid className={classes.pageContainer} container>
<Grid className={classes.formContainer} item xs={12}>
<Form
onSubmit={(values) => { console.log(values); }} // eslint-disable-line no-console
render={({ handleSubmit }) => (
<form onSubmit={handleSubmit}>
<Typography className={classes.formHeading} variant="h5">Personal Information</Typography>
<Box className={classes.marginedBottom}>
<FieldFileUploader
required
fileType="avatars"
accept={['image/*']}
name="resume"
/>
</Box>
<FormPersonalInfo setData={setData} data={data} />
<Box className={classes.spaced}>
<Box className={classes.fieldContainer}>
<Typography className={classes.noMarginBottom} variant="h6">Experience</Typography>
<IconButton color="primary" onClick={() => { setOpenExp(!showExp); }}><AddCircleIcon /></IconButton>
</Box>
{
showExp && (
<FormExperience
onCancel={() => setOpenExp(false)}
onSave={onAddExp}
handleBlur={handleBlur}
/>
)
}
{experiences.map((exp, index) => <ItemExperience key={index} exp={exp} />)}
</Box>
<Box className={classes.spaced}>
<Box className={classes.fieldContainer}>
<Typography className={classes.noMarginBottom} variant="h6">Education</Typography>
<IconButton color="primary" onClick={() => { setOpenEdu(!showEdu); }}><AddCircleIcon /></IconButton>
</Box>
{
showEdu && (
<FormEducation
onCancel={() => setOpenEdu(false)}
onSave={onAddEdu}
setData={setData}
data={data}
/>
)
}
{educations.map((edu, index) => <ItemEducation key={index} edu={edu} />)}
</Box>
<Typography className={classes.formText} variant="h6">On the web</Typography>
<Box className={classes.fieldContainer}>
<FieldInput className={classes.textField} type="text" required name="applicant-linkedin" label="Linkedin" onChange={(event: React.ChangeEvent<HTMLInputElement>) => setData({ ...data, [event.target.name]: event.target.value })} />
</Box>
<Box className={`${classes.fieldContainer} ${classes.marginedBottom}`}>
<FieldInput className={classes.textField} type="text" required name="applicant-facebook" label="Facebook" onChange={(event: React.ChangeEvent<HTMLInputElement>) => setData({ ...data, [event.target.name]: event.target.value })} />
</Box>
<Typography className={classes.formText} variant="h6">Resume</Typography>
<Box className={`${classes.fieldContainer} ${classes.marginedBottom}`}>
<Box className={classes.dropZone}>
<FieldFileUploader
required
fileType="resumes"
fieldLabel="Click or Drag file here to upload resume"
accept={['application/pdf']}
name="resume"
/>
</Box>
</Box>
<Typography className={classes.formText} variant="h6">Check the box of the ones you don't have.</Typography>
<Box className={classes.marginedBottom}>
<FormGovernmentIds setData={setData} data={data} />
</Box>
<Typography className={classes.formText} variant="h6">Preliminary Questions</Typography>
<Box className={`${classes.fieldContainer} ${classes.marginedBottom}`}>
<FormControl variant="outlined" className={classes.textField}>
<InputLabel htmlFor="outlined-age-native-simple">{variables.title}</InputLabel>
<Select
native
value={state.client}
onChange={handleChange}
label="How did you hear about us?"
inputProps={{ name: 'client', id: 'outlined-age-native-simple' }}
onClick={
(event: React.MouseEvent<HTMLDivElement, MouseEvent>) => setData({
...data,
[event.target.name]: event.target.value,
})
}
>
<option aria-label="None" value="" />
{relationOptions.map((item, index) => (
<option key={index} value={item.replace(' ', '-').toLowerCase()}>{item}</option>))}
</Select>
</FormControl>
</Box>
<FormRadioQuestions
fieldClassName={classes.fieldContainer}
textClassName={classes.textField}
values={radioValues}
setData={setData}
data={data}
/>
<Box className={classes.fieldContainer}>
<FieldCheckbox className={classes.textField} name="confirm-registration" label={checkBoxLabel} />
</Box>
<Box className={classes.fieldContainer}>
<Button component="button" type="submit" className={classes.spaced} variant="contained" color="primary">
Submit
</Button>
</Box>
</form>
)}
/>
</Grid>
</Grid>
);
};
export default PublicApplicationForm;
FormPersonalInfo組件由申請人名字、姓氏、email、位置、手機等字段組成。
當我單擊 AddCircleIcon 按鈕以顯示其他一些字段並添加經驗/教育時,我收到了這個錯誤。
當我單擊提交按鈕時,我只得到了這個日志。
我的目標是像這樣將經驗和教育數組添加到結果集中。
錯誤很明顯,嵌套了<form>
標簽。
重現錯誤的快速示例
const { useState, useEffect } = React; const App = () => { return <form> <form></form> </form> } ReactDOM.render( <App />, document.getElementById('root') );
<script src="https://unpkg.com/react/umd/react.development.js"></script> <script src="https://unpkg.com/react-dom/umd/react-dom.development.js"></script> <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script> <div id="root"></div>
在您的情況下,單擊操作后showExp
評估為true
,然后FormExperience
組件(其中顯然包含form
標簽)呈現並validateDOMNesting
引發錯誤。
showExp && (
<FormExperience
onCancel={() => setOpenExp(false)}
onSave={onAddExp}
handleBlur={handleBlur}
/>)
在我設法使用 Material UI Portals 解決它之前,我有一個類似的案例
import React from "react";
import Portal from "@material-ui/core/Portal";
export default NestedForms = () => {
const container = React.useRef(null);
return (
<>
<form>
{/*form content */}
<div ref={container} />
{/*form content */}
</form>
<Portal container={container.current}>
<form>
{/* otherform */}
<form/>
</Portal>
</>
)
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.