Фактическое значение хука reactJS useState устарело в асинхронных обещаниях

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

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

function App() {

  const [myArray, setMyArray] = useState([0,1,2])
  const sleep = (ms:number) => {
    return new Promise(resolve => setTimeout(resolve, ms));
  }
  const updateArray = () => {
    setMyArray([...myArray, myArray.length])
  }
  const firePromise = () => {
    new Promise(async (resolve) => {
      const timeStamp = new Date().getTime()
      let repeatTime = 0
      while(repeatTime < 12){
        console.info("array: ", myArray, "promiseIdenifier: ", timeStamp);
        repeatTime += 1
        await sleep(1000)
      }
      resolve({timeStamp, myArray})
    }).then(val => {
      console.info("resolved: ", val);
      
    }).catch(err => {
      console.info("rejected: ", err);
      
    })
  }
  return (
    <div className = "App">
      <button onClick = {firePromise}>new promise</button>
      <button onClick = {updateArray}>updateArray</button>
    </div>
  );
}

export default App;
  

В этом случае вам лучше использовать refs, чем state, попробуйте с useRef

Konstantin 10.12.2020 10:52

Отличный намек, ты точно спас мне целый день, бро =) @Konstantin

navid 11.12.2020 16:52
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
0
2
98
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

после рендеринга компонента React можно предположить, что текущее «состояние» компонента существует как снимок.

«myArray» в console.info — это «myArray» на момент создания firePromise. так что правильно оставить первое значение. (Каждый раз, когда компонент визуализируется, создается новый firePromise.)

Есть выход. Первый — использовать ref, а второй — использовать setState.

Первый

function App() {
  const myArray = useRef<Array<number>>([0, 1, 2]);
  const sleep = (ms: number) => {
    return new Promise((resolve) => setTimeout(resolve, ms));
  };
  const updateArray = () => {
    myArray.current.push(myArray.current.length);
  };
  const firePromise = () => {
    new Promise(async (resolve) => {
      const timeStamp = new Date().getTime();
      let repeatTime = 0;
      while (repeatTime < 12) {
        console.info(
          "array: ",
          myArray.current,
          "promiseIdenifier: ",
          timeStamp
        );
        repeatTime += 1;
        await sleep(1000);
      }
      resolve({ timeStamp, myArray: myArray.current });
    })
      .then((val) => {
        console.info("resolved: ", val);
      })
      .catch((err) => {
        console.info("rejected: ", err);
      });
  };
  return (
    <div className = "App">
      <button onClick = {firePromise}>new promise</button>
      <button onClick = {updateArray}>updateArray</button>
    </div>
  );
}

export default App;

Второй

function App() {
  const [myArray, setMyArray] = useState([0, 1, 2]);
  const sleep = (ms: number) => {
    return new Promise((resolve) => setTimeout(resolve, ms));
  };
  const updateArray = () => {
    setMyArray([...myArray, myArray.length]);
  };
  const firePromise = () => {
    new Promise(async (resolve) => {
      const timeStamp = new Date().getTime();
      let repeatTime = 0;
      while (repeatTime < 12) {
        setMyArray((prevMyArray) => {
          console.info("array: ", prevMyArray, "promiseIdenifier: ", timeStamp);
          return prevMyArray;
        });
        repeatTime += 1;
        await sleep(1000);
      }
      setMyArray((prevMyArray) => {
        resolve({ timeStamp, prevMyArray });
        return prevMyArray;
      });
    })
      .then((val) => {
        console.info("resolved: ", val);
      })
      .catch((err) => {
        console.info("rejected: ", err);
      });
  };
  return (
    <div className = "App">
      <button onClick = {firePromise}>new promise</button>
      <button onClick = {updateArray}>updateArray</button>
    </div>
  );
}

export default App;

При передаче обратного вызова в функцию setState текущее состояние передается в качестве первого аргумента. это ярлык, использующий this.

В качестве состояния рекомендуется использовать значение, которое представление должно изменить при изменении значения. Изменение «myArray» не влияет на представление, поэтому использование ref — правильный метод.

прочитайте это: https://iqkui.com/a-complete-guide-to-useeffect/

Я использовал решение useRef, и оно отлично сработало.

navid 11.12.2020 16:54

Другие вопросы по теме