簡體   English   中英

使用 react/typescript 中的最終形式從嵌套的 forms 獲取值

[英]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&apos;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 按鈕以顯示其他一些字段並添加經驗/教育時,我收到了這個錯誤。 在此處輸入圖像描述

當我單擊提交按鈕時,我只得到了這個日志。

在此處輸入圖像描述

這是與教育forms相同的經驗forms 在此處輸入圖像描述

我的目標是像這樣將經驗和教育數組添加到結果集中。

在此處輸入圖像描述

錯誤很明顯,嵌套了<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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM