React useState() 不会同步更新值

Posted

技术标签:

【中文标题】React useState() 不会同步更新值【英文标题】:React useState() doesn't update value synchronously [duplicate] 【发布时间】:2019-10-19 09:26:10 【问题描述】:

如果在设置值之后调用,React useState() 不会更新变量的值。

我阅读了有关 useEffect() 的信息,但我真的不知道这对这个特定场景有什么用处。

Full code(请打开控制台标签查看变量状态)

更新

// hook
const [ error, setError ] = useState<boolean>();
const handleSubmit = (e: any): void => 
    e.preventDefault();
    if (email.length < 4) 
      setError(true);
    
    if (password.length < 5) 
      setError(true);
    
    console.log(error); // <== still false even after setting it to true
    if (!error)  
      console.log("validation passed, creating token");
      setToken();
     else 
      console.log("errors");
    
  ;

【问题讨论】:

在您的问题中添加一些代码,并解释您要实现的目标,说“不更新变量的值在设置值后调用”是不够的。参考How to ask a good question 嗨,Tronpora,刚刚给你写了一个答案,如果你有任何问题,请告诉我。 【参考方案1】:

假设用户没有有效的凭据。问题出在这里:

if (email.length < 4)   // <== this gets executed
  setError(true);

if (password.length < 5)  // <== this gets executed
  setError(true);

console.log(error); // <== still false even after setting it to true
if (!error)  // <== this check runs before setError(true) is complete. error is still false.
  console.log("validation passed, creating token");
  setToken();
 else 
  console.log("errors");

您正在使用多个独立运行的 if 检查,而不是使用单个检查。您的代码执行所有 if 检查。在一项检查中,当满足其中一个条件时调用setError(true),但setError() 是异步的。在调用下一个 if-check 之前,该操作不会完成,这就是为什么它看起来您的值从未保存过。

您可以使用 if-else 和 useEffect 的组合来更简洁地执行此操作:https://codesandbox.io/s/dazzling-pascal-78gqp

import * as React from "react";

const Login: React.FC = (props: any) => 
  const [email, setEmail] = React.useState("");
  const [password, setPassword] = React.useState("");
  const [error, setError] = React.useState(null);

  const handleEmailChange = (e: any): void => 
    const  value  = e.target;
    setEmail(value);
  ;

  const handlePasswordChange = (e: any): void => 
    const  value  = e.target;
    setPassword(value);
  ;

  const handleSubmit = (e: any): void => 
    e.preventDefault();
    if (email.length < 4 || password.length < 5) 
      setError(true);
     else 
      setError(false);
    
  ;

  const setToken = () => 
    //token logic goes here
    console.log("setting token");
  ;

  React.useEffect(() => 
    if (error === false) 
      setToken();
    
  , [error]); // <== will run when error value is changed.

  return (
    <div>
      <form onSubmit=handleSubmit>
        <input
          type="text"
          placeholder="email@address.com"
          onChange=handleEmailChange
        />
        <br />
        <input
          type="password"
          placeholder="password"
          onChange=handlePasswordChange
        />
        <br />
        <input type="submit" value="submit" />
      </form>

      error ? <h1>error true</h1> : <h1>error false</h1>
    </div>
  );
;

export default Login;

【讨论】:

不客气@Tronpora!我很高兴这对你有所帮助:)【参考方案2】:

就像setState 一样,useState 是异步的,并且倾向于一起批量更新以提高性能。 useEffect 让您走在正确的轨道上,这将允许您在状态更新后有效地执行回调。

来自docs的示例:

import React,  useState, useEffect  from 'react';

function Example() 
  const [count, setCount] = useState(0);

  // Similar to componentDidMount and componentDidUpdate:
  useEffect(() => 
    // Update the document title using the browser API
    document.title = `You clicked $count times`;
  );

  return (
    <div>
      <p>You clicked count times</p>
      <button onClick=() => setCount(count + 1)>
        Click me
      </button>
    </div>
  );

虽然也建议您在请求更新状态后立即需要更新的值,但您最好只在组件中使用一个变量。

More on using state synchronously

如果您熟悉 Redux 的 reducer,您可以使用 useReducer 作为另一种选择。来自docs:

当你有复杂的情况时,useReducer 通常比 useState 更可取 涉及多个子值或下一个状态时的状态逻辑 取决于前一个。 useReducer 还可以让您优化 触发深度更新的组件的性能,因为您可以 向下传递调度而不是回调。

【讨论】:

如何使用useEffect?对于这个简单的场景,useReducer 是不是有点太复杂了? 我不确定您要完成什么,但就像我说的那样,您最好只使用州外的一个变量。如果我理解正确,您将尝试在设置后立即读取状态。因此,将读取状态的代码移至传递给useEffect 的箭头函数。然后在状态完成更新之前不会调用它。 我想要实现的是,在表单提交后我进行了一些验证,如果验证没有任何错误,我调用函数 setToken(),否则我只记录一条消息。正如您所提到的,解决方案是使用 useEffect,但我不确定如何在这种情况下实现。我尝试了以下 useEffect(()=> , [error]);但没有用。我已经更新了问题。

以上是关于React useState() 不会同步更新值的主要内容,如果未能解决你的问题,请参考以下文章

React Hooks:使用useState更新状态不会立即更新状态[重复]

useReducer 和 useState

React.useState 不会从 props 重新加载状态

useState setter 方法不更新数组

上下文 API 中的 React useState 值始终使用初始值而不是更新值

尽管在 React 中更新了状态,但方法没有获得正确的 useState 值