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

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,
  },
});

关键修改点

  1. 移除内部useForm调用:不再创建独立的表单实例,完全依赖外部传入的control上下文。
  2. 用Controller的onChange更新值:点击建议项时,直接调用onChange(item),该方法已绑定到外部表单实例,能同步更新输入框的值。
  3. 修正onChangeText逻辑:分开处理值更新和API调用,避免异步逻辑干扰表单状态同步。
  4. 优化状态更新:一次性生成建议列表数组,减少不必要的重渲染;添加异常捕获提升鲁棒性。
  5. 参数编码:对输入参数使用encodeURIComponent编码,避免特殊字符导致API请求失败。

内容的提问来源于stack exchange,提问作者etcN00b

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 01:40:46