单组件使用两个React Hook Form时登录输入框无法输入
问题
我尝试在一个卡片内通过切换开关实现注册与登录表单,为此声明了两个独立的useForm()实例。注册表单可正常输入、验证并提交数据,但登录表单的输入框无法输入内容。我已重命名第二个useForm实例的大部分props,请问问题出在哪里?
注册表单代码
const {control, handleSubmit, formState: { errors }, reset, getValues} = useForm({ resolver: yupResolver(validatorSchema), defaultValues: { 'username':"", 'email':'', 'password':'', 'confirmPassword':'' }, mode:'onBlur' }) const [addUser, {loading, error}] = useMutation(REGISTER_USER, { update (proxy, result) { console.log(result) navigate('/home') }, variables: { username: getValues("username"), email: getValues("email"), password: getValues("password"), role: dropdown } }) const submit = (data) => { console.log(data) addUser() reset() }
登录表单代码
const {control: authControl, handleSubmit: onSubmit, reset: res, formState: { errors: errors2 },getValues: getAuthValues} = useForm({ resolver: yupResolver(loginValidatorSchema), defaultValues: { 'loginUsername': "", 'loginPassword': "" }, mode: 'all' }) isSignup ? console.log(errors) : console.log(errors2) const [loginUser] = useMutation(AUTHENTICATE_USER,{ update(_,result) { navigate('/home') }, variables: { username: getAuthValues('loginUsername'), password: getAuthValues('loginPassword') } }) const authSubmit = (data) => { console.log(data) loginUser() res() }
组件渲染代码
const element = <TextInput.Icon name="lock-outline" /> return ( <SafeAreaView> <ScrollView contentContainerStyle={Styles.container}> <Image style={ Styles.image } source={assets.kotaco} /> <Card containerStyle={{ borderRadius: "20px", overflow: "hidden" }}> <Card.Title style={{color: '#dc1846'}}>{isSignup?'Sign up': 'Sign in'}</Card.Title> <View style={{ marginLeft: "250px", overflow: "hidden" }}> <Switch onValueChange={toggleSwitch} value={isSignup} /> </View> { isSignup ? <View> <Controller control={control} name="username" render={({field: {onChange, value, onBlur}}) => ( <TextInput placeholder=' username' style={Styles.input} value={value} //left={<TextInput.Icon name="alert-circle" size={20} color={'red'} />} left={element} onBlur={onBlur} onChangeText={(value) => onChange(value)} autoFocus half /> )} /> <Controller control={control} name="email" render={({field: {onChange, value, onBlur}}) => ( <TextInput placeholder=' email' style={Styles.input} value={value} onBlur={onBlur} keyboardType='email-address' onChangeText={(value) => onChange(value)} autoFocus half /> )} /> <Controller control={control} name="password" render={({field:{onChange, value, onBlur}}) => ( <TextInput placeholder=' password' style={Styles.input} value={value} onBlur={onBlur} secureTextEntry={showPassword} right={<TextInput.Icon name={showPassword ? "eye" : "eye-off"} onPress={() => setShowPassword(!showPassword)} />} onChangeText={(value) => onChange(value)} autoFocus half /> )} /> <Controller control={control} name="confirmPassword" render={({field:{onChange, value, onBlur}}) => ( <TextInput placeholder=' confirm password' style={Styles.input} value={value} type='password' onBlur={onBlur} secureTextEntry={showPassword} onChangeText={(value) => onChange(value)} autoFocus half /> )} /> <View style={{flexDirection:"row", marginTop: 10, marginBottom: 10}}> <Button title="Sign up" type="solid" titleStyle={{ fontWeight: '700' }} buttonStyle={{ backgroundColor: "rgba(199, 43, 98, 1)", borderColor: 'transparent', borderWidth: 0, borderRadius: 30, }} containerStyle={{ width: 100, marginRight: 50 }} onPress={ handleSubmit(submit)} /> <Dropdown style={Styles.dropdown} iconStyle={Styles.icon} data={roles} labelField="label" valueField="value" placeholder=" Select a role" value={dropdown} onChange={item => setDropdown(item.value)} renderLeftIcon={() => ( <Icon style={Styles.icon} color="#fc2d79" name="person-circle-outline" size={20} /> )} /> </View> </View> : <View> <Controller control={authControl} name="loginUsername" render={({field:{onChange, value, onBlur}}) => ( <TextInput placeholder=' username' style={Styles.input} value={value} onBlur={onBlur} onChangeText={(value) => onChange(value)} autoFocus half /> )} /> <Controller control={authControl} name="loginPassword" render={({field:{onChange, value, onBlur}}) => ( <TextInput placeholder=' password' style={Styles.input} value={value} type='password' onBlur={onBlur} secureTextEntry={showPassword} onChangeText={(value) => onChange(value)} autoFocus half /> )} /> <View style={{alignSelf:'center',marginTop: 15, marginBottom: 15}}> <Button title="Sign in" type="solid" titleStyle={{ fontWeight: '700' }} buttonStyle={{ backgroundColor: "rgba(199, 43, 98, 1)", borderColor: 'transparent', borderWidth: 0, borderRadius: 30, }} containerStyle={{ width: 100, //marginRight: 50, }} onPress={onSubmit(authSubmit)} /> </View> </View> } </Card> </ScrollView> </SafeAreaView> ) }
问题分析与解决方案
核心问题1:useMutation变量初始化时机错误
登录表单的useMutation在初始化时直接通过getAuthValues获取表单值,这只会在组件首次渲染时执行一次,后续输入内容不会更新variables的值。而且该写法会在组件初始化时就调用getAuthValues,此时表单值都是默认空值,后续输入也无法触发变量更新,直接导致登录逻辑异常,同时干扰表单输入状态。
修复方式:将变量传递移到loginUser调用时,利用authSubmit接收的表单数据传入:
const [loginUser] = useMutation(AUTHENTICATE_USER,{ update(_,result) { navigate('/home') } }) const authSubmit = (data) => { console.log(data) // 调用时传入最新的表单数据 loginUser({ variables: { username: data.loginUsername, password: data.loginPassword } }) res() }
核心问题2:登录按钮的onPress绑定错误
登录按钮的onPress={onSubmit(authSubmit)}会在组件渲染时立即执行onSubmit(authSubmit),而非点击按钮时执行。这会导致表单被立即提交,同时破坏表单的输入状态绑定,直接表现为输入框无法输入。
修复方式:用箭头函数包裹,延迟执行提交逻辑:
<Button title="Sign in" type="solid" titleStyle={{ fontWeight: '700' }} buttonStyle={{ backgroundColor: "rgba(199, 43, 98, 1)", borderColor: 'transparent', borderWidth: 0, borderRadius: 30, }} containerStyle={{ width: 100, }} onPress={() => onSubmit(authSubmit)()} />
额外优化:修复注册表单的同类问题
注册表单的useMutation也存在相同的变量初始化时机问题,建议同步修复:
const [addUser, {loading, error}] = useMutation(REGISTER_USER, { update (proxy, result) { console.log(result) navigate('/home') } }) const submit = (data) => { console.log(data) addUser({ variables: { username: data.username, email: data.email, password: data.password, role: dropdown } }) reset() }
内容的提问来源于stack exchange,提问作者22nd_wallflower
相关产品推荐
相关产品推荐

