尝试使用子组件 (EditInput) 中 Formik TextInput 的数据动态设置父组件 (EditProfileScreen) 中提交按钮的样式,但 isValid 布尔值始终延迟,从而使正确的返回当前输入之后的值。
const EditInputs = ({userInfo, onInputChange}) => {
const SignupFormSchema = Yup.object().shape({
name: Yup.string()
// .min(1, 'Name must be at least 1 characters')
.max(20, 'Name has reached character limit'),
username: Yup.string()
.min(5, 'Username must be at least 5 characters')
.max(20, 'Username has reached character limit')
.matches(
/^[A-Za-z0-9_]+$/,
'Username can only contain letters, numbers, and underscores',
) // New regex
.required('A username is required'),
bio: Yup.string().max(200, 'Username has reached character limit'),
email: Yup.string().email().required('An email is required'),
number: Yup.string()
// .required('required')
.matches(phoneRegExp, 'Phone number is not valid')
.min(10, 'too short')
.max(10, 'too long'),
// .nullable(),
});
return (
<View style={styles.wrapper}>
<Formik
initialValues={userInfo} // Use spread syntax for cleaner initialization
validationSchema={SignupFormSchema}
// validateOnBlur={true}
validateOnChange={true}
validateOnMount={true}
onSubmit={values => {
onInputChange(values, isValid); // Pass the whole 'values' object along with isValid
}}>
{({handleChange, handleBlur, handleSubmit, values, isValid}) => (
<>
<View>
<View style={styles.inputContainer}>
<Text
style={{
color: '#CCCCCC',
marginHorizontal: 18,
// marginVertical: 5,
}}>
Name
</Text>
<View
style={[
styles.inputField,
// {
// borderColor:
// 1 < values.name.length ? '#52636F' : '#fa4437',
// },
]}>
<TextInput
style={styles.input}
onChangeText={text => {
console.log(isValid);
handleChange('name')(text); // Update Formik state
onInputChange('name', text, isValid); // Pass isValid here
}}
onBlur={handleBlur('name')}
value={values.name}
placeholder="Name"
placeholderTextColor="#ACAFB0"
autoCapitalize="none"
autoCorrect={false}
textContentType="name"
/>
</View>
{/* {isUsernameValid ? (
<Image
style={[styles.icons, {tintColor: '#D39D34'}]}
source={require('../../assets/icons/excla-circle.png')}
/>
) : (
<Image
style={[styles.icons, {tintColor: '#D39D34'}]}
source={require('../../assets/icons/excla-circle.png')}
/>
)} */}
</View>
<View style={styles.inputContainer}>
<View style={{flexDirection: 'row'}}>
<Text
style={{
color: '#CCCCCC',
marginLeft: 18,
// marginVertical: 5,
}}>
Username
</Text>
<Text
style={{
color: '#B93A21',
marginHorizontal: 3,
// marginVertical: 5,
}}>
*
</Text>
</View>
<View
style={[
styles.inputField,
{
borderColor:
// values.username === ''
// ? '#52636F' // Default gray border if empty
// :
values.username.length < 5
? '#fa4437' // Red border for length violations
: !/^[A-Za-z0-9_]+$/.test(values.username)
? '#fa4437' // Red border for invalid characters
: '#52636F', // Default gray border if valid
},
]}>
<TextInput
style={styles.input}
onChangeText={text => {
// console.log(isValid);
handleChange('username')(text);
onInputChange('username', text, isValid); // Pass isValid here
}}
onBlur={handleBlur('username')}
value={values.username.trim()}
placeholder="Username"
placeholderTextColor="#ACAFB0"
autoCapitalize="none"
autoCorrect={false}
textContentType="username"
/>
</View>
<Text
style={{
color: '#55646F',
marginHorizontal: 18,
fontSize: 12,
// marginVertical: 5,
}}>
You can only change your username once every 7 days.
</Text>
</View>
<View style={styles.inputContainer}>
<Text
style={{
color: '#CCCCCC',
marginHorizontal: 18,
// marginVertical: 5,
}}>
Bio
</Text>
<View
style={[
styles.inputField,
{
borderColor:
1 > values.bio.length || values.bio.length <= 200
? '#52636F'
: '#fa4437',
// height: 100,
},
]}>
<TextInput
style={[styles.input, {textAlignVertical: 'top'}]}
placeholderTextColor="#ACAFB0"
placeholder="Bio"
autoCapitalize="none"
autoCorrect={true}
textContentType="none"
onChangeText={text => {
handleChange('bio')(text);
onInputChange('bio', text, isValid); // Pass isValid here
}}
onBlur={handleBlur('bio')}
value={values.bio}
multiline={true}
numberOfLines={5}
maxLength={200}
/>
</View>
<Text
style={{
color: '#55646F',
marginHorizontal: 18,
fontSize: 12,
// marginVertical: 5,
}}>
Brief description of your profile. URLs are hyperlinked.
</Text>
</View>
<View style={styles.inputContainer}>
<View style={{flexDirection: 'row'}}>
<Text
style={{
color: '#CCCCCC',
marginLeft: 18,
// marginVertical: 5,
}}>
Email Address
</Text>
<Text
style={{
color: '#B93A21',
marginHorizontal: 3,
// marginVertical: 5,
}}>
*
</Text>
</View>
<View
style={[
styles.inputField,
{
borderColor:
values.email.length < 1 || validate(values.email)
? '#52636F'
: '#fa4437',
},
]}>
<TextInput
style={styles.input}
placeholderTextColor="#ACAFB0"
placeholder="Email"
autoCapitalize="none"
keyboardType="email-address"
textContentType="emailAddress"
autoFocus={false}
onChangeText={text => {
handleChange('email')(text);
onInputChange('email', text, isValid); // Pass isValid here
}}
onBlur={handleBlur('email')}
value={values.email}
/>
<TouchableOpacity
style={{
alignSelf: 'center',
// justifyContent: 'center',
backgroundColor: '#5034FF',
paddingHorizontal: 15,
paddingVertical: 7,
borderRadius: 20,
}}>
<Text style={{color: 'white'}}>Verify</Text>
</TouchableOpacity>
</View>
</View>
<View style={styles.inputContainer}>
<Text
style={{
color: '#CCCCCC',
marginHorizontal: 18,
// marginVertical: 5,
}}>
Phone Number
</Text>
<View
style={[
styles.inputField,
{
borderColor:
values.number === ''
? '#52636F' // Default gray border if empty
: values.number.length > 11 ||
!phoneRegExp.test(values.number)
? '#fa4437' // Red border for invalid input
: '#52636F', // Default grey border for valid input
},
]}>
<TextInput
style={styles.input}
placeholderTextColor="#ACAFB0"
placeholder="Phone Number"
autoCapitalize="none"
autoCorrect={false}
keyboardType="phone-pad"
textContentType="telephoneNumber"
onChangeText={text => {
handleChange('number')(text);
onInputChange('number', text, isValid); // Pass isValid here
}}
onBlur={handleBlur('number')}
value={values.number}
/>
<TouchableOpacity
style={{
alignSelf: 'center',
backgroundColor: '#5034FF',
paddingHorizontal: 15,
paddingVertical: 7,
borderRadius: 20,
}}>
<Text style={{color: 'white'}}>Verify</Text>
</TouchableOpacity>
</View>
<ErrorMessage
name="number"
component={Text}
style={styles.errorText}
/>
</View>
</View>
</>
)}
</Formik>
</View>
);
};
const EditProfileScreen = ({route, navigation}) => {
const [formIsValid, setFormIsValid] = useState(true); // Assume valid initially
...
const handleInputChange = (name, value, isValid) => {
setInputValues(prevValues => ({...prevValues, [name]: value}));
// console.log(isValid);
setFormIsValid(isValid); // Update form validity from EditInputs
// setHasChanges(true);
};
const SaveButton = () => (
<View style={{position: 'absolute', bottom: 20, right: 30}}>
{hasChanges && (
<Pressable
onPress={handleSavePress}
style={({pressed}) => [
styles.saveButton,
{
backgroundColor:
!formIsValid || isSubmitting // Check form validity AND submission state
? '#838383' // Greyed out if invalid or submitting
: pressed
? '#772414'
: '#B93A21',
opacity: pressed ? 0.5 : 1,
},
]}
disabled={!formIsValid || isSubmitting} // Disable if invalid or submitting
>
{isSubmitting ? (
<ActivityIndicator color="white" />
) : (
<Image
style={styles.icon}
source={require('../assets/icons/floppy-disk.png')}
/>
)}
</Pressable>
)}
</View>
);
return (
<SafeAreaView style={styles.container}>
<>
<ScrollView>
<EditImages userInfo={userInfo} onImageChange={handleImageChange} />
<EditInputs
userInfo={inputValues}
onInputChange={handleInputChange}
/>
</ScrollView>
<EditHeader navigation={navigation} userInfo={userInfo} />
<SaveButton />
</>
</SafeAreaView>
);
};
例如-
用户名输入(只能是数字、字母和下划线。必须有 5 - 20 个字符):
预期:
LOG true
LOG true
LOG false
LOG true
LOG false
LOG true
结果:
LOG true
LOG true
LOG true
LOG false
LOG true
LOG false
可以通过在 JavaScript 表达式中使用 setState 将 Formik 验证从 ChildComponent 传递到 ParentComponent 。我把事情复杂化了。在我的 Formik 组件中添加了这个:
{onValidationChange(isValid)}
const [isFormValid, setIsFormValid] = useState(false);
<EditInputs ... onValidationChange={setIsFormValid} />
这样就没有延迟了。但是,如果有人向我解释为什么其他方法有延迟,我将不胜感激。