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

React中useState更新对象单项时为何清空已填soverview字段?

问题描述

在React应用中使用useState定义表单状态对象:

const [data, setData] = useState({
    soverview: "",
    address: "",
    first_name: "",
    last_name: "",
    email: "",
    phone: "",
    dob: ""
});

普通输入项通过onChange={handleChange}绑定处理函数:

const handleChange = ({ currentTarget: input }) => {
    setData({ ...data, [input.name]: input.value });
};

引入谷歌地址Autocomplete组件,通过onPlaceSelected绑定handleAddress自动填充地址:

function handleAddress(place) {
    setData({ ...data, address: place.formatted_address });
}

部分JSX代码:

<textarea
    className="mb-15"
    name="soverview"
    onChange={handleChange}
    value={data.soverview}
></textarea>

<Autocomplete
    className="mb-30"
    placeholder="Start typing your address"
    apiKey={api_key}
    options={{
        types: ["geocode"],
        componentRestrictions: { country: "aus" },
    }}
    onPlaceSelected={(place) => {
        handleAddress(place)
    }}
/>

<input
    className="mb-15 field-hl p-10-5"
    type="text"
    name="first_name"
    placeholder="John"
    onChange={handleChange}
    value={data.first_name}
    required
/>

调用handleAddress更新地址时,已填写的soverview字段被清空,address及之后的字段数据保留。

原因分析

出现这个问题的核心原因是闭包捕获旧状态+未使用函数式更新:

  1. 闭包捕获旧值:handleAddress函数在组件渲染时创建,会捕获当前渲染周期的data状态。如果谷歌Autocomplete组件没有在组件重新渲染时同步更新onPlaceSelected的回调(比如组件内部未正确监听props变化),触发地址选择时调用的是旧的handleAddress,而这个旧函数捕获的是soverview未被填充的旧data状态。
  2. 状态更新依赖旧值:直接使用{...data}创建新状态时,依赖的是当前闭包中的data,而非最新的组件状态。即使回调是最新的,在React异步批量更新的场景下,也可能拿到未同步的旧状态值。
解决方案

1. 使用函数式更新获取最新状态

修改状态更新逻辑,通过React提供的函数式更新方式,直接获取最新的状态值,避免闭包带来的旧值问题:

// 修改handleChange
const handleChange = ({ currentTarget: input }) => {
    setData(prevData => ({ ...prevData, [input.name]: input.value }));
};

// 修改handleAddress
function handleAddress(place) {
    setData(prevData => ({ ...prevData, address: place.formatted_address }));
}

这种方式下,prevData始终是组件当前的最新状态,展开后不会丢失已填写的字段值。

2. 用useCallback确保回调是最新的

如果Autocomplete组件依赖props变化来更新事件回调,用useCallback包裹handleAddress,确保组件能拿到最新的函数引用:

import { useCallback } from 'react';

const handleAddress = useCallback((place) => {
    setData(prevData => ({ ...prevData, address: place.formatted_address }));
}, []); // 无需依赖data,因为函数式更新会自动获取最新状态

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 16:20:38