为啥我不能更新函数组件中的状态(使用钩子)?
Posted
技术标签:
【中文标题】为啥我不能更新函数组件中的状态(使用钩子)?【英文标题】:Why can't I have updated states in function component (using hook)?为什么我不能更新函数组件中的状态(使用钩子)? 【发布时间】:2022-01-07 18:59:19 【问题描述】:我有两个输入的登录功能组件。它是受控组件,因此电子邮件和密码绑定到状态(用户)。输入是我使用的一个组件,而不是输入标签(重构输入)。我可以使用 handleInputChange 事件处理程序通过输入值更改状态用户(电子邮件和密码),我也可以使用 handleSubmit 处理程序提交表单。
在我尝试使用 yup 验证表单并捕获错误之前,一切都很好。我声明了错误状态以保存我得到的错误。我想捕获错误并显示在“div className="alert"”中,并且我想在不存在错误时将用户发布到服务器。我在 validate() 函数中看到了与 yup 相关的错误,但是当我更改错误状态时(setErrors([...er.errors])
)我发现错误状态为空(console.log(errors.length)
)。
这里是登录组件:
import axios from "axios";
import queryString from "query-string"
import useEffect, useRef,useState from "react";
import React from "react"
import useLocation, useRouteMatch,useParams from "react-router-dom"
import Input from "./input";
import * as yup from 'yup';
const Login = () =>
useEffect(async()=>
console.log(errors)
,[errors])
var [user,setUser]=useState(email:'',password:'');
var [errors,setErrors]=useState([])
let schema=yup.object().shape(
email:yup.string().email("ایمیل نامعتبر است").required("فیلد ایمیل الزامیست"),
password:yup.string().min(8,"رمز عبور باید حداقل 8 رقم باشد")
)
const validate=async()=>
try
const resultValidate=await schema.validate(user, abortEarly: false )
catch(er)
console.log(er.errors)
setErrors([...er.errors])
const handleSubmit= async(e)=>
e.preventDefault();
await validate();
console.log(errors.length)
if(errors.length===0)
alert("X")
const response= await axios.post("https://reqres.in/api/login",user)
console.log(response)
const handleInputChange=async(e)=>
setUser(...user,[e.currentTarget.name]:e.currentTarget.value)
return (
<>
<div id="login-box" className="col-md-12">
errors.length!==0 && (<div className="alert">
<ul>
errors.map((element,item)=>
return(
<>
<li key=item>
element
</li>
</>
)
)
</ul>
</div>)
<form onSubmit=handleSubmit id="login-form" className="form" action="" method="post">
<h3 className="text-center text-info">Login</h3>
<Input onChange=handleInputChange name="email" id="email" label="نام کاربری" value=user.email/>
<Input name="password" onChange=handleInputChange id="password" value=user.password label="رمز عبور"/>
/* <div id="register-link" className="text-right">
<a href="#" className="text-info">Register here</a>
</div> */
<input type="submit" className="btn btn-primary" value="ثبت"/>
</form>
</div>
</>
);
export default Login;
这里是输入组件:
import Component from "react"
class Input extends Component
render()
return <>
<div className="form-group">
<label htmlFor="username" className="text-info">this.props.label</label><br/>
<input type="text" onChange=this.props.onChange name=this.props.name id=this.props.id className="form-control" value=this.props.value />
</div>
</>;
export default Input;
我知道 setStates(在我的组件 setErrors 中)是异步的并且它是延迟的。我尝试使用简单的数组变量(命名为错误)而不是状态和钩子,但猜猜看,当我更改错误变量时它没有重新呈现页面!当然,我用这种方式在页面中看不到错误。
我尝试使用useEffect()
解决此问题,我决定检查验证错误并在useEffect
中发布,而不是handleSubmit
处理程序:
useEffect(async()=>
if(errors.length===0)
const response= await axios.post("https://reqres.in/api/login",user)
console.log(response)
console.log(errors)
, [errors])
现在我在输入无效时看到错误。当我输入有效值时,仍然有相同的错误! 看起来我无法更新错误状态,即使输入有效值,我也会收到以前的错误!我尽量不使用基于类的组件。我该怎么办?
【问题讨论】:
【参考方案1】:如果输入值经过验证,您可以返回 true,否则返回 false,来自 validate
函数,如下所示:
const validate = async () =>
try
const resultValidate = await schema.validate(user, abortEarly: false );
return true;
catch (er)
console.log(er.errors);
setErrors([...er.errors]);
return false;
;
现在在handleSubmit
函数中你必须修改一下:
const handleSubmit = async (e) =>
e.preventDefault();
const isValid = await validate();
console.log(errors.length);
if (isValid)
alert("X");
const response= await axios.post("https://reqres.in/api/login",user)
console.log(response)
setErrors([]); //so that the previous errors are removed
;
【讨论】:
【参考方案2】:问题
您面临的问题是 React 状态更新是异步处理的。这并不意味着状态更新是async
并且可以等待。在下一个渲染周期之前,您排队的errors
状态将不可用。
const validate = async () =>
try
const resultValidate = await schema.validate(user, abortEarly: false );
catch(er)
console.log(er.errors);
setErrors([...er.errors]); // (2) <-- state update enqueued
const handleSubmit = async (e) =>
e.preventDefault();
await validate(); // (1) <-- validate called and awaited
console.log(errors.length); // <-- (3) errors state from current render cycle
if (errors.length === 0)
alert("X");
const response = await axios.post("https://reqres.in/api/login", user);
console.log(response);
解决方案
我建议改为从validate
返回一个“错误”对象,如果您愿意,您可以稍后将任何状态更新加入队列。
const validate = async () =>
const errors = [];
try
await schema.validate(user, abortEarly: false );
catch(er)
console.log(er.errors);
errors.push(...er.errors);
return errors;
const handleSubmit = async (e) =>
e.preventDefault();
const errors = await validate();
console.log(errors.length);
if (!errors.length)
alert("X");
const response = await axios.post("https://reqres.in/api/login", user);
console.log(response);
else
setErrors(prevErrors => [...prevErrors, ...errors]);
【讨论】:
以上是关于为啥我不能更新函数组件中的状态(使用钩子)?的主要内容,如果未能解决你的问题,请参考以下文章