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

setUser状态更新延迟致API接收空对象的React解决方案

问题:React Hook Form + React Query 提交表单时API接收空对象

使用react-hook-form处理表单数据,并用react-query向API发起POST请求,但Axios总是把空对象{}作为user参数传递。原因是setUser()的状态更新是异步操作,调用refetch()时状态还未完成更新,导致请求使用了初始值。

原代码

import React, { useContext, useState } from 'react'
import { AuthContext } from '../../context/AuthContext'
import { ISessionContext } from '../../utils/types/interfaces/ISessionContext'
import { useQuery } from 'react-query'
import axios from 'axios'
import { useNavigate } from 'react-router'
import handleError from '../../utils/functions/handleErrors'
import ErrorDiv from '../../components/ErrorDiv'
import { useForm } from 'react-hook-form'

function Login() {
    const { setSessionInfos } = useContext(AuthContext)
    const [user, setUser] = useState({})
    const [error, setError] = useState('')
    const navigate = useNavigate()
    const key = ['auth']
    const { register, handleSubmit } = useForm()

    const { isLoading, data, isError, isFetching, refetch } = useQuery<ISessionContext | any>(key, async () => {
        console.log(user)
        const response = await axios.post(`http://localhost:3001/api/auth/login`, user) // 首次请求无法获取user数据
        return response.data;
    }, {
        enabled: false,
        onSuccess: (data) => {
            setSessionInfos(data)
            if (data.isAuth) {
                navigate('/home')
            }
        },
        onError: (error: any) => {
            const errorStatus: number = error.response.status
            const errorMessage = handleError(errorStatus)
            setError(errorMessage)
        },
        retry: 1
    })

    async function handleLogin(data: any) {
        setUser(data)
        console.log(user) // 这里输出初始状态{}
        await refetch()
    }

    return (
        <div className="col-center justify-center min-h-screen gap-8">
            <img src={CirculoIcon} alt="" className="w-56 lg:w-64" />
            <div className="col-center gap-8 p-5 w-[26rem] h-96 bg-white rounded-lg border border-princeton">
                <div className="flex flex-col gap-[2px]">
                    <h1 className="text-2xl text-center font-satoshi-bold">Bem-vindo</h1>
                    <p>Faça login para continuar</p>
                </div>
                <form className="col-center justify-center w-3/4" onSubmit={handleSubmit(handleLogin)}>
                    <label className="flex flex-col gap-1 w-full">
                        <p>Usuário:</p>
                        <input type="text" placeholder="Digite seu usuário" className="border border-gray-500 px-1 w-full rounded-md outline-none" {...register('user')} />
                    </label>
                    <label className="flex flex-col mt-3 gap-1 w-full">
                        <p>Senha:</p>
                        <input type="password" placeholder="Digite sua senha" className="border border-gray-500 px-1 rounded-md outline-none" {...register('password')} />
                    </label>
                    <label className="flex justify-center w-full mt-12">
                        <button className="border border-gray-500 w-full rounded-md py[2px] outline-none" type="submit">Entrar</button>
                    </label>
                </form>
                {isError && <ErrorDiv errorMessage={error} />}
            </div>
        </div>
    )
}

export default Login

解决方法

方案1:直接传递表单数据给refetch(推荐)

不需要通过useState存储表单数据,直接将react-hook-form返回的表单数据传给refetch,并在react-query的查询函数中接收该参数。

修改后的关键代码:

  1. 更新useQuery的查询函数,接收refetch传递的参数:
const { isLoading, data, isError, isFetching, refetch } = useQuery<ISessionContext | any>(key, async ({ arg }) => {
    const response = await axios.post(`http://localhost:3001/api/auth/login`, arg)
    return response.data;
}, {
    enabled: false,
    onSuccess: (data) => {
        setSessionInfos(data)
        if (data.isAuth) {
            navigate('/home')
        }
    },
    onError: (error: any) => {
        const errorStatus: number = error.response.status
        const errorMessage = handleError(errorStatus)
        setError(errorMessage)
    },
    retry: 1
})
  1. 简化handleLogin函数,直接传递表单数据:
async function handleLogin(data: any) {
    await refetch(data)
}
  1. 移除多余的user状态:
// 删除这一行
const [user, setUser] = useState({})

方案2:通过状态监听触发请求(不推荐)

如果一定要保留user状态,可以用useEffect监听user的变化,当user有有效值时调用refetch:

import React, { useContext, useState, useEffect } from 'react'

// ... 其他代码

const [user, setUser] = useState<{user?: string; password?: string}>({})

// 监听user变化,当有值时触发请求
useEffect(() => {
    if (user.user && user.password) {
        refetch()
    }
}, [user, refetch])

async function handleLogin(data: any) {
    setUser(data)
}

这种方法需要处理状态更新的异步性,不如方案1直接高效。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 17:55:23