Как получить измененное состояние после действия asyn c с помощью функциональных хуков React - PullRequest
8 голосов
/ 17 июня 2020

Как получить измененное состояние после действия asyn c, используя функциональные хуки React? Я нашел решение для этой проблемы или решение для компонента класса реакции, но мне интересно, есть ли простое функциональное решение реакции.

Вот сценарий:

  • создать функциональный компонент реакции с помощью. несколько состояний
  • создают несколько элементов кнопки, каждый из которых изменяет свое состояние.
  • с использованием одного из элементов кнопки запускает асинхронное c действие.
  • Если есть другое изменение состояния, до получения результатов от функции asyn c, отмените все другие продолжающиеся действия.

Прилагается пример песочницы кода https://codesandbox.io/s/magical-bird-41ty7?file= / src / App. js

import React, { useState } from "react";
import "./styles.css";

export default function App() {
  const [counter, setCounter] = useState(0);
  const [asyncCounter, setAsyncCounter] = useState(0);
  return (
    <div className="App">
      <div>
        <button
          onClick={async () => {
            //sets the asyncCounter state to the counter states after a 4 seconds timeout
            let tempCounter = counter;
            await new Promise(resolve => {
              setTimeout(() => {
                resolve();
              }, 4000);
            });
            if (tempCounter !== counter) {
              alert("counter was changed");
            } else {
              setAsyncCounter(counter);
            }
          }}
        >
          Async
        </button>
        <label>{asyncCounter}</label>
      </div>
      <div>
        <button
          onClick={() => {
            //increases the counter state
            setCounter(counter + 1);
          }}
        >
          Add 1
        </button>
        <label>{counter}</label>
      </div>
    </div>
  );
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

Ответы [ 4 ]

8 голосов
/ 07 июля 2020

Вы можете использовать ref, чтобы отслеживать значение счетчика независимо

 const [counter, setCounter] = useState(0);
 const counterRef = useRef(counter)

Каждый раз, когда вы обновляете счетчик, вы также обновляете counterRef:

const newCounter = counter + 1
setCounter(newCounter);
counterRef.current = newCounter

И затем проверяйте его:

if (counterRef.current !== counter) {
   alert("counter was changed");
} else {
   setAsyncCounter(counter);
}

Codesandox

1 голос
/ 08 июля 2020

Я нашел другой ответ, задав вопрос на facebook-rect github. Очевидно, поскольку установка состояния является функцией, ее первым аргументом является текущее состояние.

, поэтому можно получить доступ к предыдущему значению, используя этот фрагмент при установке значения счетчика:

 setCounter(prevValue => {
          alert(prevValue + " " + counter);
          return counter + 1;
        });

https://github.com/facebook/react/issues/19270 https://reactjs.org/docs/hooks-reference.html#functional -обновления

1 голос
/ 07 июля 2020

Как упоминал @thedude, вам нужно будет использовать хук useRef - он был создан именно для вашего варианта использования, как сказано в документации: «Это удобно для хранения любого изменяемого значения примерно так же, как вы» d использовать поля экземпляра в классах. "

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

  const counterChanged = useRef(false);

, а затем, когда вы обновите counter, вы тоже обновите его.

            counterChanged.current = true;
            setCounter(counter + 1);

и внутри вашей функции asyn c вы установите для него значение false, а затем проверьте, не было ли оно изменено.

counterChanged.current = false;
            await new Promise(resolve => {
              setTimeout(() => {
                resolve();
              }, 4000);
            });
            if (counterChanged.current) {
            // alert
0 голосов
/ 07 июля 2020

Вы можете просто использовать оболочку вокруг setCounter.

import React, { useState } from "react";
import "./styles.css";

export default function App() {
  const [counter, setCounter] = useState(0);
  const [asyncCounter, setAsyncCounter] = useState(0);
  let changed;
  const mySetCounter = newCounter => { changed = true; setCounter(newCounter); };
  return (
    <div className="App">
      <div>
        <button
          onClick={async () => {
            //sets the asyncCounter state to the counter states after a 4 seconds timeout
            await new Promise(resolve => {
              setTimeout(() => {
                resolve();
              }, 4000);
            });
            if (changed) {
              alert("counter was changed");
            } else {
              setAsyncCounter(counter);
            }
          }}
        >
          Async
        </button>
        <label>{asyncCounter}</label>
      </div>
      <div>
        <button
          onClick={() => {
            //increases the counter state
            mySetCounter(counter + 1);
          }}
        >
          Add 1
        </button>
        <label>{counter}</label>
      </div>
    </div>
  );
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

Надеюсь, это поможет.

...