You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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;

错误截图

点击登录后日志显示username为undefined

问题原因

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 17:15:36