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的查询函数中接收该参数。
修改后的关键代码:
- 更新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 })
- 简化handleLogin函数,直接传递表单数据:
async function handleLogin(data: any) { await refetch(data) }
- 移除多余的
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
相关产品推荐
相关产品推荐

