formik-patterns
community[skill]
使用Formik进行表单处理和验证。在构建表单、实现验证或处理表单提交时使用。
$
/plugin install medical_evaluation_systemdetails
Formik 模式
基础表单设置
import { useFormik } from "formik";
import * as yup from "yup";
const validationSchema = yup.object({
email: yup.string().email("Invalid email").required("Email is required"),
password: yup
.string()
.min(8, "Min 8 characters")
.required("Password is required"),
});
const LoginForm = () => {
const formik = useFormik({
initialValues: {
email: "",
password: "",
},
validationSchema,
onSubmit: async (values) => {
await loginMutation({ variables: { input: values } });
},
});
return (
<VStack gap="$4">
<Input
label="Email"
value={formik.values.email}
onChangeText={formik.handleChange("email")}
onBlur={formik.handleBlur("email")}
error={formik.touched.email ? formik.errors.email : undefined}
keyboardType="email-address"
autoCapitalize="none"
/>
<Input
label="Password"
value={formik.values.password}
onChangeText={formik.handleChange("password")}
onBlur={formik.handleBlur("password")}
error={formik.touched.password ? formik.errors.password : undefined}
secureTextEntry
/>
<Button
onPress={formik.handleSubmit}
isDisabled={!formik.isValid || formik.isSubmitting}
isLoading={formik.isSubmitting}
>
Login
</Button>
</VStack>
);
};
验证模式
常见模式
import * as yup from "yup";
// 邮箱
email: yup.string().email("邮箱格式无效").required("邮箱为必填项");
// 带要求的密码
password: yup
.string()
.min(8, "至少需要8个字符")
.matches(/[a-z]/, "必须包含小写字母")
.matches(/[A-Z]/, "必须包含大写字母")
.matches(/[0-9]/, "必须包含数字")
.required("密码为必填项");
// 确认密码
confirmPassword: yup
.string()
.oneOf([yup.ref("password")], "密码必须匹配")
.required("请确认密码");
// 电话号码
phone: yup
.string()
.matches(/^\+?[1-9]\d{1,14}$/, "电话号码无效")
.required("电话号码为必填项");
// 可选字段,当存在时需要验证
website: yup.string().url("必须是有效的URL").nullable();
// 带范围的数字
quantity: yup
.number()
.min(1, "最小值为1")
.max(100, "最大值为100")
.required("数量为必填项");
// 最少项数的数组
tags: yup.array().of(yup.string()).min(1, "至少选择一个标签");
条件验证
const schema = yup.object({
hasCompany: yup.boolean(),
companyName: yup.string().when("hasCompany", {
is: true,
then: (schema) => schema.required("公司名称为必填项"),
otherwise: (schema) => schema.nullable(),
}),
});
表单字段助手
输入字段助手
const getFieldProps = (name: keyof typeof formik.values) => ({
value: formik.values[name],
onChangeText: formik.handleChange(name),
onBlur: formik.handleBlur(name),
error: formik.touched[name] ? formik.errors[name] : undefined,
});
// Usage
<Input label="Email" {...getFieldProps("email")} />;
选择框/选择器助手
<Select
label="Country"
value={formik.values.country}
onValueChange={(value) => formik.setFieldValue("country", value)}
error={formik.touched.country ? formik.errors.country : undefined}
options={countryOptions}
/>
使用GraphQL的表单提交
const CreateItemForm = () => {
const [createItem] = useCreateItemMutation({
onCompleted: () => {
toast.success({ title: "项目已创建" });
navigation.goBack();
},
onError: (error) => {
console.error("createItem failed:", error);
toast.error({ title: "创建项目失败" });
},
});
const formik = useFormik({
initialValues: { name: "", description: "" },
validationSchema,
onSubmit: async (values, { setSubmitting }) => {
try {
await createItem({ variables: { input: values } });
} finally {
setSubmitting(false);
}
},
});
return (
<VStack gap="$4">
{/* 表单字段 */}
<Button
onPress={formik.handleSubmit}
isDisabled={!formik.isValid || formik.isSubmitting}
isLoading={formik.isSubmitting}
>
创建
</Button>
</VStack>
);
};
编辑表单(带初始值)
const EditItemForm = ({ item }: { item: Item }) => {
const [updateItem] = useUpdateItemMutation({
onCompleted: () => toast.success({ title: "已保存" }),
onError: (error) => {
console.error("updateItem failed:", error);
toast.error({ title: "保存失败" });
},
});
const formik = useFormik({
initialValues: {
name: item.name,
description: item.description ?? "",
},
enableReinitialize: true, // 当item属性改变时更新
validationSchema,
onSubmit: async (values) => {
await updateItem({
variables: { id: item.id, input: values },
});
},
});
// 追踪表单是否有更改
const hasChanges = formik.dirty;
return (
<VStack gap="$4">
{/* 表单字段 */}
<Button
onPress={formik.handleSubmit}
isDisabled={!hasChanges || !formik.isValid || formik.isSubmitting}
isLoading={formik.isSubmitting}
>
保存更改
</Button>
</VStack>
);
};
表单状态助手
const {
values, // 当前表单值
errors, // 验证错误
touched, // 已被触碰的字段
isValid, // 表单是否通过验证
isSubmitting, // 是否正在提交
dirty, // 值是否与初始值不同
handleSubmit, // 提交处理器
handleChange, // 改变处理器
handleBlur, // 模糊处理器
setFieldValue, // 设置单个字段
setFieldTouched, // 标记字段已触碰
resetForm, // 重置为初始值
setSubmitting, // 控制提交状态
} = formik;
多步骤表单
const MultiStepForm = () => {
const [step, setStep] = useState(0);
const formik = useFormik({
initialValues: {
// Step 1
name: "",
email: "",
// Step 2
address: "",
city: "",
// Step 3
cardNumber: "",
},
validationSchema: stepSchemas[step],
onSubmit: async (values) => {
if (step < steps.length - 1) {
setStep(step + 1);
} else {
await submitOrder(values);
}
},
});
return (
<VStack>
{step === 0 && <PersonalInfoStep formik={formik} />}
{step === 1 && <AddressStep formik={formik} />}
{step === 2 && <PaymentStep formik={formik} />}
<HStack gap="$4">
{step > 0 && (
<Button variant="outline" onPress={() => setStep(step - 1)}>
上一步
</Button>
)}
<Button
onPress={formik.handleSubmit}
isDisabled={!formik.isValid}
isLoading={formik.isSubmitting}
>
{step < steps.length - 1 ? "下一步" : "提交"}
</Button>
</HStack>
</VStack>
);
};
反面模式
// 错误 - 未显示验证错误
<Input
value={formik.values.email}
onChangeText={formik.handleChange('email')}
/>
// 正确 - 触碰时显示错误
<Input
value={formik.values.email}
onChangeText={formik.handleChange('email')}
onBlur={formik.handleBlur('email')}
error={formik.touched.email ? formik.errors.email : undefined}
/>
// 错误 - 提交按钮始终启用
<Button onPress={formik.handleSubmit}>提交</Button>
// 正确 - 在无效或提交时禁用
<Button
onPress={formik.handleSubmit}
isDisabled={!formik.isValid || formik.isSubmitting}
isLoading={formik.isSubmitting}
>
提交
</Button>
// 错误 - mutation上没有错误处理
onSubmit: async (values) => {
await createItem({ variables: { input: values } });
}
// 正确 - 处理错误
onSubmit: async (values, { setSubmitting }) => {
try {
await createItem({ variables: { input: values } });
} catch (error) {
toast.error({ title: '保存失败' });
} finally {
setSubmitting(false);
}
}
与其他技能的集成
- graphql-schema: Mutation提交模式
- react-ui-patterns: 加载/错误状态
- testing-patterns: 测试表单验证和提交
technical
- github
- Adamuuuu/medical_evaluation_system
- stars
- 0
- license
- unspecified
- contributors
- 2
- last commit
- 2026-03-09T03:22:08Z
- file
- .claude/skills/formik-patterns/SKILL.md