Не удается запустить функции в .then() Axios React

#reactjs #asynchronous #promise #axios

#reactjs #асинхронный #обещание #axios

Вопрос:

У меня есть веб-страница, где я извлекаю данные с помощью async axios, а затем выполняю вычисления с ними.

Вот фрагмент кода:

 const FetchData = async () =>{
    console.log("FETCH CALLED");
    await Axios.get(`http://localhost:8080/stock/getquote/${props.API}`)
          .then(resp => {
            setStockData(resp.data);
            calculateTrend();
            calculateTrendDirection();
          })
  }
 

Здесь я получаю ошибку при функции calculateTrend() . Мой вопрос в том, что this .then() должен запускаться при поступлении ответа, но, похоже, он запускается раньше. Потому что и calculateTrend, и calculateTrendDirection работают с этими извлеченными данными

Редактировать: ошибка, которую я получаю, заключается в том, что невозможно прочитать свойство ‘previousClosePrice’ undefined. Я уверен, что это существует в объекте, поэтому неправильное написание не является проблемой

Edit2: я отредактировал свой компонент в соответствии с вашими решениями, и одно из них сработало, единственное, что выборка переходит в бесконечный цикл и выполняется несколько раз в секунду. Я подозреваю, что это зависимости в useEffect, но я не уверен, что там установить.

Вот мой полный компонент:

 function StockCard(props) {
  const [FetchInterval, setFetchInterval] = useState(300000);
  const [StockData, setStockData] = useState({});
  const [TrendDirection, setTrendDirection] = useState(0);
  const [Trend, setTrend] = useState(0);

  const FetchData = async () =>{
    console.log("FETCH CALLED");
    const resp = await Axios.get(`http://localhost:8080/stock/getquote/${props.API}`)
      setStockData(resp.data);
  }

  const calculateTrendDirection = () => {
    console.log(StockData.lastPrice);
    if(StockData.lastPrice.currentPrice >  StockData.lastPrice.previousClosePrice){
      setTrendDirection(1);
    } else if (StockData.lastPrice.currentPrice <  StockData.lastPrice.previousClosePrice){
      setTrendDirection(-1);
    } else {
      setTrendDirection(0);
    }
  }

  const calculateTrend = () => {
    console.log(StockData.lastPrice);
    var result =  100 * Math.abs( ( StockData.lastPrice.previousClosePrice - StockData.lastPrice.currentPrice ) / ( (StockData.lastPrice.previousClosePrice   StockData.lastPrice.currentPrice)/2 ) );

    setTrend(result.toFixed(2));
  }


  useEffect(() => {
    FetchData();

    if(StockData.lastPrice){
      console.log("LÉTEZIK A LAST PRICE")
      calculateTrend();
      calculateTrendDirection();
    }
    
    const interval = setInterval(() => {
      FetchData();
    }, FetchInterval)

    return() => clearInterval(interval);

  },[StockData, FetchData, FetchInterval, calculateTrend, calculateTrendDirection]);

  




    return(
        <div>
        <CryptoCard
          currencyName={StockData.lastPrice? StockData.name : "Name"}
          currencyPrice={StockData.lastPrice? `$ ${StockData.lastPrice.currentPrice}` : 0}
          icon={<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/4/46/Bitcoin.svg/2000px-Bitcoin.svg.png"/>}
          currencyShortName={StockData.lastPrice? StockData.symbol : "Symbol"}
          trend={StockData.lastPrice? `${Trend} %` : 0}
          trendDirection={StockData.lastPrice? TrendDirection : 0} 
          chartData={[9200, 5720, 8100, 6734, 7054, 7832, 6421, 7383, 8697, 8850]}
        />
        </div>
    )
 

Комментарии:

1. Похоже, что вы setStockData не устанавливаете состояние до того, как данные будут доступны. Добавьте useEffect и вызовите обе функции, как только данные будут доступны, или добавьте данные в качестве параметра к обеим функциям.

2. Пожалуйста, проверьте мое редактирование # 2.

Ответ №1:

then Блок вызывается только после выполнения обещания, поэтому данные доступны в этот момент.

Из того, что я вижу, проблема заключается setStockData в попытке установить переменную stockData состояния с помощью ответа, но calculateTrend и calculateTrendDirection вызываются до того, как состояние установлено, потому что обновление значений состояния выполняется пакетно.

Существует несколько решений проблемы.

Решение 1:

Вы можете вызвать две функции после установки состояния:

 setStockData(resp.data, () => {
  calculateTrend();
  calculateTrendDirection();
});
 

Решение 2:

Вы можете использовать useEffect для повторного вызова функций после обновления состояния:

 useEffect(() => {
  if (stockData) { // or whatever validation needed
    calculateTrend();
    calculateTrendDirection();
  }
}, [stockData]);
 

Решение 3:

Вы можете передать параметры методу:

 calculateTrend(resp.data);
calculateTrendDirection(resp.data);
 

Лучший вариант? Я думаю, # 2, потому что это также гарантирует, что тренд и направление тренда пересчитываются всякий раз, когда данные о запасах обновляются (по любым другим причинам).

Комментарии:

1. Я пошел с решением 2, но все равно получил ту же ошибку в том же месте

2. Вероятно, это потому, что вы не выполнили надлежащую проверку. Сделайте что-то вроде if (stockData.id) или любое другое обязательное поле, которое должно быть доступно после получения ответа.

3. У вас может быть несколько useEffect s, поэтому не объединяйте все в один большой useEffect , а вместо этого добавьте мой код для строгого пересчета тенденций, а для других используйте ваш текущий useEffect .

4. И что я должен включить в раздел зависимостей useEffect?

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

Ответ №2:

Я предполагаю calculateTrend , что вы используете данные, которые setStockData устанавливаются в состояние, если это так

setState не происходит сразу после вызова setState , если вы хотите что-то выполнить после корректного обновления состояния, тогда следует посмотреть на что-то вроде этого

 setStockData(resp.data, () => {
    calculateTrend();// this will call once the state gets changed
});
 

или вы могли бы использовать useEffect

 useEffect(() => {
    calculateTrend(); // this will call every time when stockData gets changed
}, [stockData])
 

Комментарии:

1. Теперь это мой useEffect() , но он по-прежнему говорит об отмене каждой функции asnyc в useEffect (и я понятия не имею, как это сделать). « useEffect(() => { fetchData(); calculateTrend(); calculateTrendDirection(); const interval = setInterval(() => {fetchData(); }, FetchInterval) return() => clearInterval(интервал); }, [FetchInterval, StockData]); «

2. Трудно понять, что вам на самом деле нужно, не могли бы вы добавить свой компонент и чего вы хотите достичь

Ответ №3:

Если вы используете stockData внутри функции calculateTrend, а setStockData является асинхронной функцией, переместите функцию calculateTrend в useEffect, используя stockData в качестве зависимости, поэтому при каждом обновлении stockData будут вызываться calculateTrend и calculateTrendDirection:

 useEffect(() => {
  const interval = setInterval(() => {
     FetchData();
  }, FetchInterval);

  return() => clearInterval(interval);
}, [FetchInterval]);


useEffect(() => {
   if(StockData.lastPrice){
     console.log("LÉTEZIK A LAST PRICE")
     calculateTrend();
     calculateTrendDirection();
   }
}, [StockData]);

const FetchData = async () =>{
    console.log("FETCH CALLED");
    const res = await Axios.get(`http://localhost:8080/stock/getquote/${props.API}`);
    setStockData(resp.data);
}
 

Комментарии:

1. Вы можете определить множественный эффект использования.

2. Он по-прежнему выполняется вечно: (

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