簡體   English   中英

如何在 React 功能組件中動態地將我的表單綁定到 Formik

[英]How can I dynamically tie my form into Formik, in React functional component

我正在構建一個 React 組件,它從外部 API 調用動態加載表單數據。 用戶完成后,我需要將其提交回另一個 API 端點。

這里是:

import React, { useEffect, useState } from "react";
import axios from "axios";
import TextField from "@material-ui/core/TextField";
import { useFormik } from "formik";

const FORM = () => {

  const [form, setForm] = useState([]);
  const [submission, setSubmission] = useState({});

  const formik = useFormik({
    initialValues: {
      email: "",
    },
  });

  useEffect(() => {
    (async () => {
      const formData = await axios.get("https://apicall.com/fakeendpoint");
      setForm(formData.data);
    })();  
  }, []);

  return (
    <form>
        {form.map((value, index) => {
          if (value.fieldType === "text") {
            return (
              <TextField 
                key={index} 
                id={value.name}
                label={value.label}
              />
            );
          } 
          if (value.fieldType === "select") {
            return (
              <TextField 
                select={true}
                key={index} 
                id={value.name}
                label={value.label}
              >
                {value.options.map((option) => (
                  <option key={option.value} value={option.value}>
                    {option.label}
                  </option>
                ))}
              </TextField>
            );
          }
        })}
        <button type="submit">Submit</button>
      </form>
  );
};
 
export default FORM;

API 調用正常(是的,我現在需要一些錯誤處理)並且我能夠填充字段並在頁面上獲取表單。 我遇到麻煩的地方(而且我對 Formik 較新,所以請耐心等待)是我需要進行驗證和提交。 我真的不知道如何處理這些值,通常我會為變量編寫某種類型的 static 代碼,測試它們然后提交。

例如,通常我會為“名稱”設置一個字段,為“電子郵件”設置一個字段。 在這種情況下,我無法寫入這些字段,因為它們來自 API,在我們收到呼叫響應之前我們不知道它們是什么。

我的代碼處理字段創建,但我需要連接以進行驗證和提交,並且想要使用 Formik。

我怎樣才能完成一個用於驗證和提交的動態表單(通過 formik)?

enableReinitialize添加到 formik

useFormik({
    initialValues: initialData,
    enableReinitialize: true,
    onSubmit: values => {
      // Do something
    }
});

表格文件

我有同樣的問題,我用 <Formik> 組件而不是 useFormik() 鈎子解決了它。 不知道為什么 useFormik 在動態驗證上失敗,但無論如何,在切換到 <Formik> 之后,下面是對我有用的代碼。 請檢查以下代碼片段后的關鍵點。

<Formik
innerRef={formikRef}
initialValues={request || emptyRequest  //Mostafa: edit or new: if request state is avaialble then use it otherwise use emptyRequest.
}
enableReinitialize="true"
onSubmit={(values) => {
    saveRequest(values);
}}

validate={(data) => {
    let errors = {};

    if (!data.categoryId) {
        errors.categoryId = 'Request Category is required.';
    }

    //Mostafa: Dynamic validation => as the component is re-rendered, the validation is set again after request category is changed. React is interesting and a lot of repetitive work is done.
    if (requestCategoryFields)
        requestCategoryFields.map(rcField => {
            if (rcField.fieldMandatory) { //1- check Mandatory
                if (!data.dynamicAttrsMap[rcField.fieldPropertyName]) {
                    if (!errors.dynamicAttrsMap) //Mostafa: Need to initialize the object in errors, otherwise Formik will not work properly.
                        errors.dynamicAttrsMap = {}
                    errors.dynamicAttrsMap[rcField.fieldPropertyName] = rcField.fieldLabel + ' is required.';
                }
            }
            if (rcField.validationRegex) { //2- check Regex
                if (data.dynamicAttrsMap[rcField.fieldPropertyName]) {
                    var regex = new RegExp(rcField.validationRegex); //Using RegExp object for dynamic regex patterns.
                    if (!regex.test(data.dynamicAttrsMap[rcField.fieldPropertyName])) {
                        if (!errors.dynamicAttrsMap) //Mostafa: Need to initialize the object in errors, otherwise Formik will not work properly.
                            errors.dynamicAttrsMap = {}

                        if (errors.dynamicAttrsMap[rcField.fieldPropertyName]) //add an Line Feed if another errors line already exists, just for a readable multi-line message.
                            errors.dynamicAttrsMap[rcField.fieldPropertyName] += '\n';

                        errors.dynamicAttrsMap[rcField.fieldPropertyName] = rcField.validationFailedMessage; //Regex validaiton error and help is supposed to be already provided in Field defintion by admin user.
                    }
                }
            }

        });
    return errors;
}}>
{({errors,handleBlur,handleChange,handleSubmit,resetForm,setFieldValue,isSubmitting,isValid,dirty,touched,values
}) => (
    <form autoComplete="off" noValidate onSubmit={handleSubmit} className="card">
        <div className="grid p-fluid mt-2">

            <div className="field col-12 lg:col-6 md:col-6">
                <span className="p-float-label p-input-icon-right">
                    <Dropdown name="categoryId" id="categoryId" value={values.categoryId} onChange={e => { handleRequestCategoryChange(e, handleChange, setFieldValue) }}
                        filter showClear filterBy="name"
                        className={classNames({ 'p-invalid': isFormFieldValid(touched, errors, 'categoryId') })}
                        itemTemplate={requestCategoryItemTemplate} valueTemplate={selectedRequestCategoryValueTemplate}
                        options={requestCategories} optionLabel="name" optionValue="id" />
                    <label htmlFor="categoryId" className={classNames({ 'p-error': isFormFieldValid(touched, errors, 'categoryId') })}>Request Category*</label>
                </span>
                {getFormErrorMessage(touched, errors, 'siteId')}
            </div>

            {
                //Here goes the dynamic fields
                requestCategoryFields && requestCategoryFields.map(rcField => {
                    return (
                        <DynamicField field={rcField} values={values} touched={touched} errors={errors} handleBlur={handleBlur} 
                        handleChange={handleChange} isFormFieldValid={isFormFieldValid} getFormErrorMessage={getFormErrorMessage} 
                        crudService={qaCrudService} toast={toast} />
                    )
                })
            }
        </div>
        <div className="p-dialog-footer">
            <Button type="button" label="Cancel" icon="pi pi-times" className="p-button p-component p-button-text"
                onClick={() => {
                    resetForm();
                    hideDialog();
                }} />
            <Button type="submit" label="Save" icon="pi pi-check" className="p-button p-component p-button-text" />
        </div>
    </form>
)}

關鍵點:

  1. 我的動態字段也來自遠程 API,通過選擇 categoryId DropDown ,並設置到requestCategoryFields state 中。
  2. 我將動態字段的值存儲在我的主要 object 內的一個名為dynamicAttrsMap的嵌套 object 中,這稱為request ,因為我也有 static 字段。
  3. 我使用了validate道具,其中包括一個用於categoryId字段的 static 驗證,然后是一個為所有其他動態字段添加動態驗證的循環(映射)。 此策略適用於每個 state 更改,您的組件被重新渲染,您的驗證是從頭開始創建的。
  4. 我有兩種驗證,一種檢查必填字段的值是否存在。 如果需要,另一個檢查動態字段內容的正則表達式驗證。
  5. 我將動態字段的渲染移至單獨的組件<DynamicField>以實現更好的模塊化。 請檢查我的 DynamicField 組件
  6. 您可以將驗證道具移動到const以獲得更好的編碼; 這里我的代碼有點亂。
  7. 為了更好地編碼,我使用了兩個const用於錯誤消息和 CSS ,如下所示:
 const isFormFieldValid = (touched, errors, name) => { return Boolean(getIn(touched, name) && getIn(errors, name)) }; const getFormErrorMessage = (touched, errors, name) => { return isFormFieldValid(touched, errors, name) && <small className="p-error">{getIn(errors, name)}</small>; };

請在我的 GitHub上查看我的完整組件,以便更好地理解。 如果您需要,請發表評論以獲得任何澄清。 因為我知道 Formik 有時會很棘手。 我們必須互相幫助。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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