使用 Formik 和 Yup 库为 React + vite 创建可重用的表单组件

问题描述 投票:0回答:1

我正在尝试创建一个react+vite项目,在其中我尝试使用Formik和Yup实现可重用的表单组件,但还无法实现这一点。 我创建了一个名为

<AppInputField/>
的组件,它只是一个带有自定义 CSS 的
<input/>
字段,以适应我对整个网站的设计。与此类似,我有
<AppButton/>
,这又只是
<button/>
。我通过 props 传递所有必需的属性。现在,当我尝试为
<AppForm/>
创建一个类似的它将围绕
formik
库时,我很难解决它。从搜索中我知道它是相关的
useFormik()
useFormikContext()
但同样不是 100% 确定。 这是我的代码:

SignUp.jsx

const SignUp = () => {
  
const signUpSchema = Yup.object({
  name: Yup.string().min(2).max(25).required("Please enter your name"),
  });

  return (
    <>
    <AppForm
        initialValues={{
            name : ''
        }}
        validationSchema={signUpSchema}
        onSubmit={(values) => {console.log("Submitted with these values\n" + values)}}
    >
        <AppInputField 
          name='name'
          label='Enter your name'
          placeholder='XYZ'
        />
        <SubmitButton title='Register'/>
    </AppForm>
    </>
  );
};

export default SignUp;

AppForm.js

function AppForm({initialValues, onSubmit, validationSchema, children}) {
    return (
        <Formik
            initialValues={ initialValues }
            onSubmit={ onSubmit }
            validationSchema={ validationSchema } 
        >
                {children}
                {/* children passed as props here are the inner components of form 
meaning AppInputField and SubmitButton
this actually renders the inside components */}
        </Formik>
    );
}
export default AppForm;

AppInputField.jsx

const AppInputField = ({name, label, placeholder, type='text') => {
    const { handleChange, handleBlur, values } = useFormikContext();
    return (
        <>
        <label htmlFor={name} className="input-label">{label}</label>
        <input
            autoComplete="off"
            className="input-text"
            id={name}
            name={name} 
            onChange={handleChange}
            onBlur={handleBlur}
            placeholder={placeholder}
            type={type}
            value={values[name]}
        />
        </>
    )
}
export default AppInputField

SubmitButton.jsx

function SubmitButton({title}) {
    const { handleSubmit } = useFormikContext();
    return (
        <AppButton 
            title={title} 
            type='submit' 
            onClick={handleSubmit}
        />
    );
}
export default SubmitButton;

所以点击SubmitButton时的错误是

Uncaught TypeError: Cannot destructure property 'handleSubmit ' of 'useFormikContext(...)' as it is undefined.
如果我将其更改为
submitForm
那么同样的错误。此外,当使用
useFormik()
时,组件的代码发生了更改,但也没有成功。使用
useFormik
时,我无法在另一个组件中使用上下文。我需要在同一个 jsx 文件中以相同的形式使用所有内容。希望我能够解释我的目标。这种抽象是我在学习 Mosh Hemdani 的 React-Native 课程时遇到的。我发现它是如此美丽,以至于我想在我的
react+vite
项目中实现这一点。任何帮助将不胜感激。

javascript reactjs vite formik yup
1个回答
0
投票

明白了!很乐意与大家分享。

useField
还不错,感谢 adsy 的指导。这是我与 Yup 和 Formik 的代码...

AppForm.jsx

const AppForm = ({initialValues, validationSchema, onSubmit, children}) => (
  <>
    <Formik
      initialValues={initialValues}
      validationSchema={validationSchema}
      onSubmit={onSubmit}
    >
      {(formikProps) => (
        <Form>


            {React.Children.map(children, child => {
                if (React.isValidElement(child)) {
                  React.cloneElement(child);
                }
                return child;
            })}
        </Form>
      )}
    </Formik>
  </>
);
export default AppForm

AppInputField.jsx

const AppFormInputField = ({label, type='text', ...props}) => {
    const [field, meta, helpers] = useField(props);
  return (
    <>
        <AppInputField label={label} type={type} {...field} {...props}/>
        <ErrorMessage error={meta.error} visible={meta.touched && meta.error} />
    </>
  )
}

export default AppFormInputField

AppFormFileUpload.jsx

const AppFormFileUpload = ({label, allowedExtensions, setFieldValue, ...props}) => {
    const [field, meta, helpers] = useField(props);
  return ( 
    <>
{/*FileUploadField is basically input field with attribute type=file with custom css*/}
      <FileUploadField label={label} {...field} {...props}  
        value={undefined}
        onChange={(event) => {
          helpers.setValue(event.target.files)
        }}
      />
      <ErrorMessage error={meta.error} visible={meta.touched && meta.error}/>
    </>
  )
}

export default AppFormFileUpload

没有变化

SubmitButton.jsx

SignUp.jsx

const SignUp = () => {

  const checkSchema = Yup.object({
    firstName: Yup.string().min(3).required("First Name is Required"),
    file : Yup.mixed().required('required')
    .test('fileFormat', 'Only approved files are allowed', (value, context) => {
      if (value) {//just pretest check
        const supportedFormats = ['pdf','jpg', 'gif', 'png', 'jpeg', 'svg', 'webp'];
        for (const [key, val] of Object.entries(value)) {
          if(!supportedFormats.includes(val.name.split('.').pop())) return false
        }
      }
      return true;
    })
    .test('fileSize', 'File size must be less than 3MB', (value, context) => {
      if (value) {//just pretest check
        for (const [key, val] of Object.entries(value)) {
          if(val.size > 3145728) return false
        }
      }
      return true;
    }),
  })

  const initialValues={
    firstName: '',
    file : ''
  }

  const handleSubmit = (values, actions) => {console.log(values)}

  return (
    <>
    <AppForm
      initialValues={initialValues}
      validationSchema={checkSchema}
      onSubmit={handleSubmit}
    >
      <AppFormInputField name='firstName' label='Enter First Name' />
      <AppFormFileUpload name='file' multiple/>
      <SubmitButton title='Submit' />    
    </AppForm>
    </>
  );
};

export default SignUp;

希望这个解决方案可能会有所帮助...通过这种方式,表单可以通过各自的 formik 和 yup 验证重复使用

© www.soinside.com 2019 - 2024. All rights reserved.