为什么我不能在功能组件中更新状态(使用钩子)?
Why can't I have updated states in function component (using hook)?
我有两个输入的登录功能组件。它是受控组件,因此电子邮件和密码绑定到状态(用户)。 Input是我用来代替input标签的一个组件(重构输入)。我可以使用 handleInputChange 事件处理程序使用输入值更改状态用户(电子邮件和密码),我也可以使用 handleSubmit 处理程序提交表单。
一切都很好,直到我尝试使用 yup 验证表单并捕获错误。我声明了错误状态以保存我得到的错误。我想捕获错误并在“div className="alert"" 中显示,并且我想在不存在错误时 post 用户访问服务器。我在 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
中检查验证错误和 post 而不是 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])
现在我在输入无效时看到错误。当我输入有效值时,仍然有相同的错误!
看起来我无法更新错误状态,即使在输入有效值后我也会收到以前的错误!我尽可能不使用基于 class 的组件。我该怎么办?
如果输入值经过验证,您可以 return 为真,否则为假,来自 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
}
};
问题
您面临的问题是 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]);
}
}
我有两个输入的登录功能组件。它是受控组件,因此电子邮件和密码绑定到状态(用户)。 Input是我用来代替input标签的一个组件(重构输入)。我可以使用 handleInputChange 事件处理程序使用输入值更改状态用户(电子邮件和密码),我也可以使用 handleSubmit 处理程序提交表单。
一切都很好,直到我尝试使用 yup 验证表单并捕获错误。我声明了错误状态以保存我得到的错误。我想捕获错误并在“div className="alert"" 中显示,并且我想在不存在错误时 post 用户访问服务器。我在 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
中检查验证错误和 post 而不是 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])
现在我在输入无效时看到错误。当我输入有效值时,仍然有相同的错误! 看起来我无法更新错误状态,即使在输入有效值后我也会收到以前的错误!我尽可能不使用基于 class 的组件。我该怎么办?
如果输入值经过验证,您可以 return 为真,否则为假,来自 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
}
};
问题
您面临的问题是 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]);
}
}