我正在尝试创建一个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
项目中实现这一点。任何帮助将不胜感激。
明白了!很乐意与大家分享。
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 验证重复使用