React Native中如何用JSON Form Schema自定义渲染UI并集成Yup验证?
在React Native中实现JSON Schema表单+自定义渲染器+Yup验证
完全可以实现,现成包满足不了需求的话,自己搭建核心逻辑反而更灵活,能精准匹配你的UI和验证需求。以下是具体实现思路和代码示例:
1. 定义适配的JSON Schema结构
先定义包含UI元数据、字段类型和验证映射的Schema,比如:
{ "fields": [ { "name": "username", "label": "用户名", "type": "text", "placeholder": "请输入用户名", "yupRule": "required|min:3|max:20" }, { "name": "email", "label": "邮箱", "type": "email", "placeholder": "请输入邮箱", "yupRule": "required|email" }, { "name": "age", "label": "年龄", "type": "number", "placeholder": "请输入年龄", "yupRule": "required|min:18|max:100" }, { "name": "acceptTerms", "label": "同意服务条款", "type": "boolean", "yupRule": "required|isTrue" } ] }
2. 实现自定义字段渲染器
创建一个FormFieldRenderer组件,根据Schema中的type渲染对应的React Native UI元素:
import React from 'react'; import { View, Text, TextInput, Switch, StyleSheet } from 'react-native'; const FormFieldRenderer = ({ field, value, onChange, error }) => { const handleChange = (val) => { onChange(field.name, field.type === 'number' ? Number(val) : val); }; return ( <View style={styles.fieldContainer}> <Text style={styles.label}>{field.label}</Text> {field.type === 'text' || field.type === 'email' ? ( <TextInput style={[styles.input, error && styles.errorInput]} placeholder={field.placeholder} value={value?.toString() || ''} onChangeText={handleChange} keyboardType={field.type === 'email' ? 'email-address' : 'default'} /> ) : field.type === 'number' ? ( <TextInput style={[styles.input, error && styles.errorInput]} placeholder={field.placeholder} value={value?.toString() || ''} onChangeText={handleChange} keyboardType="numeric" /> ) : field.type === 'boolean' ? ( <View style={styles.switchContainer}> <Switch value={value || false} onValueChange={handleChange} /> </View> ) : null} {error && <Text style={styles.errorText}>{error}</Text>} </View> ); }; const styles = StyleSheet.create({ fieldContainer: { marginBottom: 16 }, label: { fontSize: 14, fontWeight: '500', marginBottom: 8 }, input: { borderWidth: 1, borderColor: '#ccc', borderRadius: 8, padding: 12, fontSize: 16 }, errorInput: { borderColor: '#ff4444' }, switchContainer: { flexDirection: 'row', alignItems: 'center' }, errorText: { color: '#ff4444', fontSize: 12, marginTop: 4 } }); export default FormFieldRenderer;
3. 集成Yup表单验证
编写工具函数将Schema中的yupRule转换为Yup验证规则,然后在表单中处理验证逻辑:
import React, { useState } from 'react'; import { View, Button, StyleSheet } from 'react-native'; import * as Yup from 'yup'; import FormFieldRenderer from './FormFieldRenderer'; import formSchema from './formSchema.json'; // 转换Schema规则为Yup Schema const buildYupSchema = (fields) => { const yupShape = {}; fields.forEach(field => { let yupField = Yup.mixed(); const rules = field.yupRule?.split('|') || []; rules.forEach(rule => { const [ruleName, ruleValue] = rule.split(':'); switch(ruleName) { case 'required': yupField = yupField.required(`${field.label}不能为空`); break; case 'min': yupField = field.type === 'number' ? yupField.min(Number(ruleValue), `${field.label}不能小于${ruleValue}`) : yupField.min(Number(ruleValue), `${field.label}长度不能小于${ruleValue}位`); break; case 'max': yupField = field.type === 'number' ? yupField.max(Number(ruleValue), `${field.label}不能大于${ruleValue}`) : yupField.max(Number(ruleValue), `${field.label}长度不能大于${ruleValue}位`); break; case 'email': yupField = yupField.email(`${field.label}格式不正确`); break; case 'isTrue': yupField = yupField.oneOf([true], `${field.label}必须同意`); break; default: break; } }); yupShape[field.name] = yupField; }); return Yup.object().shape(yupShape); }; const CustomSchemaForm = () => { const [formValues, setFormValues] = useState({}); const [errors, setErrors] = useState({}); const yupValidationSchema = buildYupSchema(formSchema.fields); const handleFieldChange = (name, value) => { setFormValues(prev => ({ ...prev, [name]: value })); // 实时清除对应字段错误 if (errors[name]) { setErrors(prev => ({ ...prev, [name]: undefined })); } }; const handleSubmit = async () => { try { await yupValidationSchema.validate(formValues, { abortEarly: false }); // 验证通过,处理提交逻辑 console.log('表单数据:', formValues); } catch (err) { const newErrors = {}; err.inner.forEach(error => { newErrors[error.path] = error.message; }); setErrors(newErrors); } }; return ( <View style={styles.container}> {formSchema.fields.map(field => ( <FormFieldRenderer key={field.name} field={field} value={formValues[field.name]} onChange={handleFieldChange} error={errors[field.name]} /> ))} <Button title="提交" onPress={handleSubmit} /> </View> ); }; const styles = StyleSheet.create({ container: { flex: 1, padding: 20, marginTop: 40 } }); export default CustomSchemaForm;
扩展说明
- 可以根据需求扩展更多字段类型(比如Picker、日期选择器),只需要在
FormFieldRenderer中添加对应分支即可。 - 如果需要更复杂的UI自定义,可以在Schema中增加
customProps字段,传递给渲染组件。 - 验证规则可以直接在Schema中定义Yup规则对象,代替字符串拆分,更灵活。
内容的提问来源于stack exchange,提问作者Praful Kadam
相关产品推荐
相关产品推荐

