reactjs - 使用動態鍵的 Yup/Formik 驗證
我正在嘗試驗證一個具有動態欄位數量的表單——也就是說,數據是從一個 API 返回的,該 API 決定顯示多少行,每一行都有一個必填欄位,使用者必須選擇一個輸入才能繼續。
將要使用的套件是 Yup 搭配 Formik。在查看 Yup 教學時,物件通常是這樣建立的:
let userSchema = object({
name: string().required(),
});
像 name 這樣的鍵是被定義好的。然而,我的鍵需要是動態的,也就是 field1、field2 等,因為我事先不知道會有多少個這樣的鍵。
我想要遍歷我的物件,並將一組動態的鍵傳遞給 schema——基本上物件的長度就是我將擁有的鍵的數量。
let userSchema = object({
[field1]: string().required(),
[field2]: string().required(),
});
然而,我不確定如何達到這個結果。我可以遍歷我的物件並嘗試建立一組鍵,例如粗略地說。
let myObject = {}
myKeyObject.forEach((key) => myObject[key] = string().required());
然後將 myKeyObject 傳給 object.shape,但這通常會產生 TS 錯誤。有人知道 Yup 中有任何用於動態表單的工具嗎?除非我漏掉了什麼,否則我在文件中看不到任何可以讓動態表單更容易使用的東西
答案#1
如果您想要動態欄位,您可以添加一個欄位陣列(包含欄位名稱或鍵、標籤、初始值和欄位類型),然後從該陣列生成一個 Schema,以下是一個範例:
import React, { Fragment } from 'react';
import { Field, Form, Formik } from 'formik';
import { string, object, number } from 'yup';
interface IField{
name: string,
label: string,
initialValue: any,
type: any
}
const fields: IField[] = [
{
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;
