React useState () не обновляет значение синхронно - PullRequest
1 голос
/ 03 июня 2019

React useState () не обновляет значение переменной, если оно вызывается сразу после установки значения.

Я читал об useEffect (), но на самом деле не знаю, как это будет полезно для этого конкретного сценария.

Полный код (откройте вкладку консоли, чтобы увидеть статус переменной)

UPDATE

// 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");
    }
  };

Ответы [ 2 ]

2 голосов
/ 03 июня 2019

Так же, как setState, useState является асинхронным и имеет тенденцию к пакетному обновлению вместе, чтобы быть более производительным.Вы находитесь на правильном пути с useEffect, который позволит вам эффективно выполнять обратный вызов после обновления состояния.

Пример из документов :

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>
  );
}

Хотя также рекомендуется, чтобы, если вам нужно обновленное значение, как только было запрошено обновление состояния, вам, скорее всего, лучше всего использовать только переменную в компоненте.

Подробнее об использовании состояния синхронно

И если вы знакомы с редукторами Redux, вы можете использовать useReducer в качестве другой альтернативы.Из документов :

useReducer обычно предпочтительнее useState, когда у вас есть сложная логика состояния, которая включает в себя несколько вспомогательных значений, или когда следующее состояние зависит от предыдущего.useReducer также позволяет оптимизировать производительность для компонентов, запускающих глубокие обновления, поскольку вы можете передавать диспетчеризацию вместо обратных вызовов.

1 голос
/ 04 июня 2019

Предположим, у пользователя нет действительных учетных данных. Проблема здесь:

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;
...