React Hook Form在React Native中日志显示username未定义问题排查
React Hook Form Controller绑定后获取不到输入值的问题修复
问题描述
React Native项目中集成React Hook Form,使用Controller组件绑定用户名输入框,点击登录按钮后,控制台日志显示username为undefined,无法正常获取输入内容。
问题代码
import React, {useState} from 'react' import { View, Text, Image, StyleSheet, useWindowDimensions, ScrollView, Alert, TextInput } from 'react-native' import Logo from '../../../assets/images/logo-main.png' import CustomButton from '../../components/CustomButton/CustomButton'; import CustomInput from '../../components/CustomInput/CustomInput'; import { useNavigation } from '@react-navigation/native'; import {useForm, Controller} from 'react-hook-form'; import { Auth } from 'aws-amplify'; const LoginScreen = () => { const [loading, setLoading] = useState(false); const {height} = useWindowDimensions(); const {control, handleSubmit} = useForm(); const navigation = useNavigation(); const onLoginPressed = (data) => { console.log(data) // if (loading) { // return; // } // setLoading(true); // try { // response = await Auth.signIn(data.username, data.password); // console.log(response); // } catch(e) { // Alert.alert('Opps', e.message); // } // setLoading(false); }; const onForgotPasswordPressed = () => { navigation.navigate('ForgotPassword'); } const onRegisterPressed = () => { navigation.navigate('Register') } return ( <ScrollView contentContainerStyle={{ flexGrow: 1, justifyContent: 'center' }} showsVerticalScrollIndicator={false}> <View style={styles.root}> <Image source={Logo} style={[styles.logo, {height : height * 0.2}]} resizeMode={'contain'} /> {/* <CustomInput placeholder='Username' /> <CustomInput placeholder='Password' secureTextEntry={true}/> */} <Controller control={control} name="username" render={({field: value, onChange, onBlur}) => <TextInput value={value} onChangeText={onChange} onBlur={onBlur} placeholder='Username' /> } /> <TextInput placeholder='Password'/> <CustomButton text={loading ? 'Loading...' : 'Login Account'} onPress={handleSubmit(onLoginPressed)} /> <CustomButton text='Forgot Password?' onPress={onForgotPasswordPressed} type='TERTIARY' /> <CustomButton text="Don't have an account? Create one" onPress={onRegisterPressed} type='TERTIARY' /> </View> </ScrollView> ); }; const styles = StyleSheet.create({ root: { alignItems: 'center', padding: 20, }, logo: { width: 200, maxWidth: 300, maxHeight: 300, }, }); export default LoginScreen;
错误截图

问题原因
Controller组件的render回调中,对field对象的解构语法错误:
原代码写成{field: value, onChange, onBlur},这相当于把整个field对象重命名为value,而不是从field对象中解构出value、onChange、onBlur属性。导致输入框的value没有正确绑定到表单状态,onChange也无法更新表单值,最终提交时username为undefined。
修复方案
1. 修正Controller的解构逻辑
将render回调中的解构改为{ field: { value, onChange, onBlur } },正确从field对象中获取所需属性。
2. (可选)设置表单默认值
给useForm添加defaultValues配置,确保初始值不为undefined,提升表单稳定性。
3. 统一绑定密码输入框
为了保持表单逻辑一致,建议密码输入框也用Controller绑定。
修复后的代码
import React, {useState} from 'react' import { View, Text, Image, StyleSheet, useWindowDimensions, ScrollView, Alert, TextInput } from 'react-native' import Logo from '../../../assets/images/logo-main.png' import CustomButton from '../../components/CustomButton/CustomButton'; import CustomInput from '../../components/CustomInput/CustomInput'; import { useNavigation } from '@react-navigation/native'; import {useForm, Controller} from 'react-hook-form'; import { Auth } from 'aws-amplify'; const LoginScreen = () => { const [loading, setLoading] = useState(false); const {height} = useWindowDimensions(); // 添加defaultValues配置,设置初始空值 const {control, handleSubmit} = useForm({ defaultValues: { username: '', password: '' } }); const navigation = useNavigation(); const onLoginPressed = (data) => { console.log(data) // if (loading) { // return; // } // setLoading(true); // try { // response = await Auth.signIn(data.username, data.password); // console.log(response); // } catch(e) { // Alert.alert('Opps', e.message); // } // setLoading(false); }; const onForgotPasswordPressed = () => { navigation.navigate('ForgotPassword'); } const onRegisterPressed = () => { navigation.navigate('Register') } return ( <ScrollView contentContainerStyle={{ flexGrow: 1, justifyContent: 'center' }} showsVerticalScrollIndicator={false}> <View style={styles.root}> <Image source={Logo} style={[styles.logo, {height : height * 0.2}]} resizeMode={'contain'} /> {/* 修正Controller的解构语法 */} <Controller control={control} name="username" render={({ field: { value, onChange, onBlur } }) => <TextInput value={value} onChangeText={onChange} onBlur={onBlur} placeholder='Username' /> } /> {/* 给密码输入框也添加Controller绑定 */} <Controller control={control} name="password" render={({ field: { value, onChange, onBlur } }) => <TextInput value={value} onChangeText={onChange} onBlur={onBlur} placeholder='Password' secureTextEntry={true} /> } /> <CustomButton text={loading ? 'Loading...' : 'Login Account'} onPress={handleSubmit(onLoginPressed)} /> <CustomButton text='Forgot Password?' onPress={onForgotPasswordPressed} type='TERTIARY' /> <CustomButton text="Don't have an account? Create one" onPress={onRegisterPressed} type='TERTIARY' /> </View> </ScrollView> ); }; const styles = StyleSheet.create({ root: { alignItems: 'center', padding: 20, }, logo: { width: 200, maxWidth: 300, maxHeight: 300, }, }); export default LoginScreen;
内容的提问来源于stack exchange,提问作者Imran Irshad
相关产品推荐
相关产品推荐

