#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. Я думаю, что существует зависимость, которая вызывает множество повторных рендерингов, попробуйте удалить один за другим, пока не увидите, что вызывает проблему. Также проверьте, является ли интервальная функция частью проблемы.