【问题标题】: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])

现在我在输入无效时看到错误。当我输入有效值时,仍然有相同的错误! 看起来我无法更新错误状态,即使输入有效值,我也会收到以前的错误!我尽量不使用基于类的组件。我该怎么办?

【问题讨论】:

    标签: javascript reactjs react-hooks react-functional-component


    【解决方案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]);
        }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-08-24
        • 2021-08-17
        • 1970-01-01
        • 2021-02-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-05-19
        相关资源
        最近更新 更多