【问题标题】:Yup / Formik validation using dynamic keys是的 / Formik 使用动态键验证
【发布时间】:2022-07-21 02:10:45
【问题描述】:

我正在尝试验证具有动态字段数量的表单 - 即,数据是从确定显示多少行的 API 返回的,并且对于每一行,都有一个需要用户选择输入的必填字段让他们前进。

要使用的包是与 Formik 一起使用的。在查看 Yup 教程时,该对象通常构建如下:

let userSchema = object({
  name: string().required(),
});

定义名称等键的位置。但是,我的键需要是动态的,即 field1、field2 等,因为我提前不知道它们会有多少。

我想遍历我的对象并将一组动态键传递给架构 - 基本上无论对象有多长,我都有多少键。

let userSchema = object({
  [field1]: string().required(),
  [field2]: string().required(),
});

但是,我不确定如何实现此结果。我可以遍历我的对象并尝试构建一组键,例如粗略的

let myObject = {}
myKeyObject.forEach((key) => myObject[key] = string().required());

然后将myKeyObject传递给object.shape,但这通常会产生TS错误。有谁知道 Yup 中有任何用于动态表单的实用程序?除非我遗漏了什么,否则我在文档中看不到任何可以使使用动态表单更容易的内容

【问题讨论】:

  • 产生的打字稿错误是什么
  • field1field2 在编译时是否已知?否则输入userSchema真的没有意义。
  • 请检查下面的答案,它可能会帮助你。

标签: reactjs typescript formik yup


【解决方案1】:

如果您想要动态字段,您可以添加一个字段数组(包含字段名称或键、标签、初始值和字段类型),然后从该数组生成一个 Schema,这是一个示例:

import React, { Fragment } from 'react';
import { Field, Form, Formik } from 'formik';
import { string, object, number } from 'yup';

interface Fields{
  name: string,
  label: string,
  initialValue: any,
  type: any
}

const fields: Fields[] = [
  {
    name: 'firstName',
    label: 'Firstname',
    initialValue: '',
    type: string().required()
  },
  {
    name: 'lastName',
    label: 'Lastname',
    initialValue: '',
    type: string().required()
  },
  {
    name: 'email',
    label: 'Email',
    initialValue: '',
    type: string().required()
  },
  {
    name: 'password',
    label: 'Password',
    initialValue: '',
    type: string().required()
  },
  {
    name: 'age',
    label: 'Age',
    initialValue: 18,
    type: number()
  }
];

const initialValues = Object.fromEntries(fields.map((field)=>[field.name, field.initialValue]))

const SchemaObject = Object.fromEntries(fields.map((field)=>[field.name, field.type]))

const UserSchema = object().shape(SchemaObject);

const App = () => (
  <Fragment>
    <h1>User</h1>
    <Formik
      initialValues={initialValues}
      onSubmit={values =>
        console.log({values})
      }
      validationSchema={UserSchema}
      >
        {({ errors, touched }) => {
          return(
          <Form>
            <div>
               {fields.map(({label, name}, index) => (
                  <div key={index}>
                    <label style={{width: 100, display: 'inline-block'}}>{label}</label>
                    <Field name={name} />
                    {touched[name] && errors[name] && <div style={{color: 'red'}}>{errors[name]?.toString()}</div>}
                  </div>
                ))}
              <div>
                <button type="submit">Submit</button>
              </div>
            </div>
        </Form>
      );
      }}
    </Formik>
  </Fragment>
);

export default App;

【讨论】:

    【解决方案2】:
    **Today i was working on too my much forms so i was trying to make it more dynamic**
    
    **Do you mean like this**    
    **My Validation schema generator**
    
    import testFormModel from './testFormModel';
        import * as yup from 'yup';
        
        const { formField } = testFormModel;
        
        const [firstName] = formField;
        
        const dynamicValidationGenerator = formField => {
          //dynamic required validation for required field
          const validateObj = {};
          formField.map(field => {
            field.required &&
              Object.assign(validateObj, {
                [field.name]: yup
                  .string()
                  .required(`${field.errorText.requiredErrorMsg}`),
              });
          });
          return validateObj;
        };
        
        //for manual validation + dynamic validation
        export default yup.object().shape({
          ...dynamicValidationGenerator(formField),
        
          [firstName.name]: yup.string().min(5),
        });
    
    **my form model**
    export default {
      formId: 'testForm',
      formField: [
        {
          name: 'firstName',
          label: 'First Name',
          required: true,
          errorText: {
            requiredErrorMsg: 'Required message',
          },
        },
        {
          name: 'lastName',
          label: 'Last Name',
          required: true,
          errorText: {
            requiredErrorMsg: 'Required message',
          },
        },
        { name: 'email', label: 'Email' },
        { name: 'age', label: 'Age' },
        { name: 'gender', label: 'Gender' },
      ],
    };
    
    **Initial form field value generator**
    const initialFormValueGenerator = formField => {
      const initialValues = {};
      formField.map(el => Object.assign(initialValues, { [el.name]: '' }));
      return initialValues;
    };
    export default initialFormValueGenerator;
    
    **Input field**
    import React from 'react';
    import { useField } from 'formik';
    
    function InputField(props) {
      const { errorText, ...rest } = props;
      const [field] = useField(props);
    
      return (
        <div style={{ display: 'flex', justifyContent: 'space-between' }}>
          <label>{props.label}</label>
          {props?.required && <span style={{ color: 'red' }}>*</span>}
          <input
            type='text'
            onChange={value => console.log(value)}
            name={props.name}
            {...field}
            {...rest}
          />
        </div>
      );
    }
    
    export default InputField;
    
    **Form field html **
    import React from 'react';
    import InputField from '../FormField/InputField';
    
    function AddressForm(props) {
      const { formField } = props;
      return (
        <div
          style={{
            display: 'flex',
            flexDirection: 'column',
            gap: 20,
            padding: 20,
          }}
        >
          {formField.map(field => {
            return (
              <div key={field.name}>
                <InputField {...field} />
              </div>
            );
          })}
        </div>
      );
    }
    
    export default AddressForm;
    
    **App.js**
    import { Formik, Form } from 'formik';
    import React from 'react';
    import AddressForm from './Features/Form/AddressForm';
    import testFormModel from './Features/FormModel/testFormModel';
    import validationSchema from './Features/FormModel/validationSchema';
    import initialFormValueGenerator from './Features/Form/formInitialValues';
    function App() {
      const { formId, formField } = testFormModel;
    
      const _handleSubmit = value => {
        console.log('submitted', value);
      };
      return (
        <div
          style={{
            display: 'flex',
            justifyContent: 'center',
            alignItems: 'center',
          }}
        >
          <div
            style={{
              width: '50%',
              border: '1px solid black',
              display: 'flex',
              flexDirection: 'column',
              marginTop: 20,
              padding: 20,
              backgroundColor: 'orange',
            }}
          >
            <Formik
              initialValues={initialFormValueGenerator(formField)}
              validationSchema={validationSchema}
              onSubmit={_handleSubmit}
            >
              {() => (
                <Form id={formId}>
                  <AddressForm formField={formField} />
                  <div>
                    <button type='submit'>Submit</button>
                  </div>
                </Form>
              )}
            </Formik>
          </div>
        </div>
      );
    }
    
    export default App;
    

    【讨论】:

      猜你喜欢
      • 2020-07-21
      • 1970-01-01
      • 2020-04-21
      • 2020-02-22
      • 2019-04-03
      • 1970-01-01
      • 2022-08-18
      • 2019-09-29
      • 1970-01-01
      相关资源
      最近更新 更多