React useState() 不会同步更新值

React useState() doesn't update value synchronously

如果在设置值后立即调用,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");
    }
  };

就像 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 的减速器,您可以使用 useReducer 作为另一种选择。来自 docs:

useReducer is usually preferable to useState when you have complex state logic that involves multiple sub-values or when the next state depends on the previous one. useReducer also lets you optimize performance for components that trigger deep updates because you can pass dispatch down instead of callbacks.

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

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-checks,而不是使用一个。您的代码执行所有 if-checks。在一项检查中,您在满足其中一个条件时调用 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;