React Hook Form单Controller多控制问题:地址补全输入框无法赋值
问题解决:React Hook Form Controller 结合 Google Place Autocomplete 点击建议无法更新输入框
问题根源
你在组件内部调用了const { setValue } = useForm();,这会生成一个全新的React Hook Form实例,和你从props传入的control不属于同一个表单上下文。因此,内部的setValue只能修改自身实例的表单值,无法同步到外部父组件的表单状态,自然不会更新输入框内容。
另外,你不能在一个Controller上绑定多个control实例——Controller只能对应单个表单上下文。
解决方案
核心思路
移除组件内部的useForm()调用,直接使用Controller提供的onChange方法更新表单值(该方法已绑定到外部传入的control实例),同时修正API调用和状态更新的逻辑。
修改后的完整代码
import React from "react"; import { FlatList, StyleSheet, TouchableOpacity, View } from "react-native"; import { Text, TextInput, Colors } from "react-native-paper"; import { Controller } from "react-hook-form"; import axiosInstance from "services/axiosInstance"; export default React.forwardRef( ( { name, label, placeholder, control, style: addOnStyle, ...props }, ref ) => { const [addressList, setAddressList] = React.useState([]); const getAddressList = async (input) => { // 空输入直接清空列表 if (!input || input.trim() === "") { setAddressList([]); return; } try { const response = await axiosInstance.get( // 对输入参数编码,避免特殊字符导致请求失败 `https://maps.googleapis.com/maps/api/place/autocomplete/json?input=${encodeURIComponent(input)}&components=country:us&language=en&key=API_KEY` ); if (response?.data?.status === "OK") { // 一次性生成建议列表,避免多次setState触发重渲染 const descriptions = response.data.predictions.map(item => item.description); setAddressList(descriptions); } else { setAddressList(["Address not found."]); } } catch (error) { setAddressList(["Failed to fetch addresses."]); console.error("Address fetch error:", error); } }; return ( <View style={{ ...styles.viewInput, ...addOnStyle }}> <Controller control={control} name={name} defaultValue="" render={({ field: { onChange, onBlur, value, name: fieldName }, fieldState: { error }, }) => { return ( <> <TextInput label={label} name={fieldName} placeholder={placeholder} onBlur={onBlur} // 分开处理值更新和API调用,避免异步逻辑干扰 onChangeText={(val) => { onChange(val); getAddressList(val); }} error={!!error?.message} value={value} ref={ref} {...props} /> {error?.message && ( <Text style={styles.textError}>{error?.message}</Text> )} {addressList.length > 0 && ( <View style={styles.addressListContainer}> <FlatList keyExtractor={(_, i) => String(i)} data={addressList} renderItem={({ item, index }) => ( <TouchableOpacity activeOpacity={1} style={[ styles.addressListItem, index === 0 ? { borderTopWidth: 0 } : {} ]} onPress={() => { setAddressList([]); // 直接使用Controller提供的onChange更新表单值 onChange(item); }} > <Text numberOfLines={1}>{item}</Text> </TouchableOpacity> )} /> </View> )} </> ); }} /> </View> ); } ); const styles = StyleSheet.create({ viewInput: { marginBottom: 16, }, textError: { color: Colors.red500, marginTop: 4, fontSize: 12, }, addressListContainer: { backgroundColor: "#fff", borderWidth: 1, borderColor: Colors.grey300, borderRadius: 4, marginTop: 4, maxHeight: 200, }, addressListItem: { padding: 12, borderTopWidth: 1, borderColor: Colors.grey300, }, });
关键修改点
- 移除内部useForm调用:不再创建独立的表单实例,完全依赖外部传入的
control上下文。 - 用Controller的onChange更新值:点击建议项时,直接调用
onChange(item),该方法已绑定到外部表单实例,能同步更新输入框的值。 - 修正onChangeText逻辑:分开处理值更新和API调用,避免异步逻辑干扰表单状态同步。
- 优化状态更新:一次性生成建议列表数组,减少不必要的重渲染;添加异常捕获提升鲁棒性。
- 参数编码:对输入参数使用
encodeURIComponent编码,避免特殊字符导致API请求失败。
内容的提问来源于stack exchange,提问作者etcN00b
相关产品推荐
相关产品推荐

