How to keep calling api until task is completed in reactjs? - reactjs

I am working on reactjs as frontend, and Django in the backend.
I have a time taking task written in django, which ideally takes more time to retrieve values than the stipulated API response time.
Therefore, I have made it into a celery task, whose task id I return as an API response.
The plan was to make the API call on page load which starts the celery task, and returns the task ID. So, with the task ID, I can keep polling another API to get the task's status, until completed. Once the task is completed, I can ping another API to get the response of the celery task.
I thought, I can make the API call, and thenafter run a loop with a sleep, but not sure how to achieve this?
import { useEffect, useState } from "react"
import axios from "axios"
function App() {
const [taskId, setTaskId] = useState("")
const apiToSpawnTask = () => {
axios.get("http://localhost:8000/spawn_task")
.then(({data}) => setTaskId(data.task_id))
}
const checkTaskStatus = () => {
axios.get(`http://localhost:8000/task-id/${taskId}`)
.then(({data}) => {
// data.status contains the status of the task id
})
}
const getCompletedTaskResult = () => {
axios.get(`http://localhost:8000/get-task-result/${taskId}`)
.then(({data}) => {
// this data is used in the return
})
}
useEffect(() => {
// What should be the code here?
})
return (<div>Test</div>)
}

const checkTaskStatus = () => {
return axios.get(`http://localhost:8000/task-id/${taskId}`)
.then(({data}) => {
return data
})
}
// ...
useEffect(() => {
const interval = setInterval(() => {
const status = checkTaskStatus
// probably a different property returned from your api
if (status.ready) {
clearInterval(interval)
}
}, 1000)
return () => clearInterval(interval)
})

import { useEffect, useState, useRef } from "react"
import axios from "axios"
function App() {
const [taskId, setTaskId] = useState("");
const intervalRef = useRef(null);
const apiToSpawnTask = () => {
axios.get("http://localhost:8000/spawn_task")
.then(({data}) => {
setTaskId(data.task_id);
intervalRef.current = setInterval(() => {
checkTaskStatus(data.task_id);
}, 5000);
})
}
const checkTaskStatus = (id) => {
axios.get(`http://localhost:8000/task-id/${id}`)
.then(({data}) => {
// data.status contains the status of the task id
if(data.status === 'success') {
getCompletedTaskResult();
clearInterval(intervalRef.current);
}
})
}
const getCompletedTaskResult = () => {
axios.get(`http://localhost:8000/get-task-result/${taskId}`)
.then(({data}) => {
// this data is used in the return
})
}
useEffect(() => {
apiToSpawnTask();
}, [])
return (<div>Test</div>)
}
Tip: instead of hardcoding the base URL as http://localhost:8000, try to use axios instance for that. So if you want to change base URL in future, you don't need to modify everywhere.
axios/index.js
import axios from "axios";
const instance = axios.create({
baseURL: "http://localhost:8000"
});
export default instance;

Related

hello fetch my data is making more than one request, why?

I left the code below that I got my data from. More than one request is processed at the time of refreshing the page, the reason may be why, if you can help I would appreciate it. have a nice day.
import React, { useEffect, useState } from "react";
import axios from "axios"
import Cookies from "universal-cookie"
const Entry = React.createContext();
export const EntryProvider = ({ children }) => {
const [post, setPost] = useState();
const cookie = new Cookies()
const token = cookie.get("acsess_token")
const getAll = () => {
axios.defaults.headers.common['Authorization'] = token;
const entry = axios.get("/api/entry/entry", {
headers: {
"Authorization": token
}
})
.then((response) => {
const data = response.data.data
data.map(element => {
setPost(element)
});
setPost(data)
})
.catch((err) => { console.log(err) })
}
useEffect(() => {
getAll()
},[getAll])
return (
<Entry.Provider value={{post}}>
{children}
</Entry.Provider>
);
};
export const userEntry = () => {
return React.useContext(Entry);
};
Instead adding getAll in the array dependency, remove it
useEffect(() => {
getAll()
},[getAll])
Like this:
useEffect(() => {
getAll()
},[])
Why that?
Because the useEffect will be execute it every time the component renders and because of having getAll in the dependency array it will execute it again

How can i stop axios request?

I'm trying to stop axios request. I use useInterval(custom hooks)(I referenced a website) to request api.
So I stop it with useState and it's totally stopped when i set interval like 1000ms.
however, when i set interval like 100ms then i can't stop api request. it's stopped after 3seconds or something.
So i tried to use if statement. but it's not working as i expected.
and I also checked Network from development tool on chrome
and the request Status was getting changed from pending to 200
and when all the request's Status change to 200, then it stopped.
I really want to know how i can stop API request properly.
my code is like this
useInterval
import { useEffect } from "react";
import { useRef } from "react";
const useInterval = (callback, delay) => {
const savedCallback = useRef(callback);
useEffect(() => {
savedCallback.current = callback;
}, [callback]);
useEffect(() => {
const tick = () => {
savedCallback.current();
};
if (delay !== null) {
const id = setInterval(tick, delay);
return () => clearInterval(id);
}
}, [delay]);
};
export default useInterval;
API calling
const [API_DATA, setAPI_DATA] = useState(null);
const [apiStart, setApiStart] = useState(false);
const [spinner, setSpinner] = useState(false);
//Request API
const getAPI = useCallback(async () => {
if (apiStart) {
await axios
.get(API_URL, {
headers: Header,
})
.then(response => {
setAPI_DATA(response.data);
setSpinner(false);
})
.catch(error => {
init();
console.log("error");
});
}
}, [API_DATA, spinner]);
// start API
const start_API = () => {
setSpinner(true);
setApiStart(true);
};
//stop API
const stop_API = () => {
setSpinner(false);
alert("API STOP");
setApiStart(false);
};
//using useInterval
useInterval(
() => {
if (apiStart) return getAPI();
},
apiStart ? 100 : null
);

Stop axios request call in react

I'm trying to stop axios request.
I use useInterval(custom hooks)(I referenced a website) to request api.
So I stop it with useState and it's totally stopped when i set interval like 1000ms.
however, when i set interval like 100ms then i can't stop api request. it's stopped after 3seconds or something.
So i tried to use if statement. but it's not working as i expected.
and I also checked Network from development tool on chrome
and the request Status was getting changed from pending to 200
and when all the request's Status change to 200, then it stopped.
I really want to know how i can stop API request properly.
my code is like this
useInterval
import { useEffect } from "react";
import { useRef } from "react";
const useInterval = (callback, delay) => {
const savedCallback = useRef(callback);
useEffect(() => {
savedCallback.current = callback;
}, [callback]);
useEffect(() => {
const tick = () => {
savedCallback.current();
};
if (delay !== null) {
const id = setInterval(tick, delay);
return () => clearInterval(id);
}
}, [delay]);
};
export default useInterval;
API calling
const [API_DATA, setAPI_DATA] = useState(null);
const [apiStart, setApiStart] = useState(false);
const [spinner, setSpinner] = useState(false);
//Request API
const getAPI = useCallback(async () => {
if (apiStart) {
await axios
.get(API_URL, {
headers: Header,
})
.then(response => {
setAPI_DATA(response.data);
setSpinner(false);
})
.catch(error => {
init();
console.log("error");
});
}
}, [API_DATA, spinner]);
// start API
const start_API = () => {
setSpinner(true);
setApiStart(true);
};
//stop API
const stop_API = () => {
setSpinner(false);
alert("API STOP");
setApiStart(false);
};
//using useInterval
useInterval(
() => {
if (apiStart) return getAPI();
},
apiStart ? 100 : null
);
Go take a look at the axios documentation at https://axios-http.com/docs/cancellation. I would remove the if(apiStart) as this does not do much. I would possibly rewrite your this method as follows:
const [data, setData] = useState(null);
const [spinnerActive, setSpinnerActive] = useState(false);
const controller = new AbortController();
const getAPI = useCallback(async () => {
setSpinnerActive(true);
await axios
.get(API_URL, {
headers: Header,
signal: controller.signal
})
.then(response => {
setData(response.data);
setSpinnerActive(false);
})
.catch(error => {
setSpinnerActive(false);
console.log("error");
});
}, [data, spinnerActive]);
useInterval(
() => {
getApi()
},
apiStart ? 100 : null
);
Then when you want to abort the request, call controller.abort()

Why my data from an api is still undefined?

I'm trying to fetch some data from an api and display it in jsx.First I get the users geolocation,then I call the fetch function which uses the users geolocation data to request the data from an api , afterwards
the received data from an api is used to set the weatherData state.The final step is where conditional rendering is used to show the h1 element depending if the state is defined or not.The problem is that my weatherData is always undefined,and when I try to display it returns as undefined error.Why is my weatherData undefined?
import react from "react";
import {useState} from "react";
import {useEffect} from "react";
const MainWeather=()=>{
{/*State for storing geolocation data*/}
const [status, setStatus] = useState(null);
const [weatherData,setWeatherData]=useState('');
{/*Fetches the data from an api*/}
const fetchData=(link)=>{
fetch(link)
.then(res => res.json())
.then(
(result)=>{
{/*Sets the weather data object*/}
setWeatherData(result);
console.log(result);
setStatus('data set');
},
(error)=>{
console.log(error)
}
)
}
{/*Retrieves the location from geolocation api*/}
const getLocation = async () => {
if (!navigator.geolocation) {
setStatus('Geolocation is not supported by your browser');
} else {
navigator.geolocation.getCurrentPosition((position) => {
{/*Calls the fetch function to get the data from an api*/}
fetchData(`https://api.openweathermap.org/data/2.5/onecall?lat=${position.coords.latitude}&lon=${position.coords.longitude}&exclude={part}&appid=0ea4f961aae42bfa56f75ca058577e1e&units=metric`);
}, () => {
setStatus('Unable to retrieve your location');
});
}
}
{/*Calls getLocation function on the first render*/}
useEffect(()=>{getLocation()},[])
console.log(status);
return(
<div>
{weatherData == 'undefined' ?
<h1>undefined</h1> :
<h1>{weatherData.current.temp}</h1> }
</div>
)
}
export default MainWeather;
I checked the code, what you have implemented is correct , if you are using a mac you should allow browser to fetch location , in windows a popup will come to allow it , might be browser issue check it again
still I made few changes in below the above code , just refer to it
import { useState, useEffect } from "react";
const MainWeather = () => {
const [status, setStatus] = useState(null);
const [weatherData, setWeatherData] = useState("");
const fetchData = (link) => {
fetch(link)
.then((res) => res.json())
.then(
(result) => {
setWeatherData(result);
setStatus("data set");
},
(error) => {
console.log(error);
}
);
};
const getLocation = async () => {
if (!navigator.geolocation) {
setStatus("Geolocation is not supported by your browser");
} else {
navigator.geolocation.getCurrentPosition(
(position) => {
fetchData(
`https://api.openweathermap.org/data/2.5/onecall?lat=${position.coords.latitude}&lon=${position.coords.longitude}&exclude={part}&appid=0ea4f961aae42bfa56f75ca058577e1e&units=metric`
);
},
() => {
setStatus("Unable to retrieve your location");
}
);
}
};
useEffect(() => {
getLocation();
}, []);
return (
<div>
{!weatherData ? (
<h1>{status}</h1>
) : (
<h1>{weatherData?.current?.temp} ℃ </h1>
)}
</div>
);
};
export default MainWeather;
You can refer to this codesandbox

setState react hook for array is not saving prior array elements

I have an API request that uses aysnc and await, grabs the data, then makes a second request with Promise.all, which makes multiple API requests with the id's. That part works out fine.
However, when I go to save the data inside a React hook called, "setItem", it only saves that one and over writes the others. I have a spread operator inside the setItem()
setItems(...items, data)
data being the response from the API request.
My API request is in the top layer of my react app, so I pulled it out into it's own little helper file, that's why "items" and "setItems", are arguments passed through.
import axios from 'axios';
import BottleNeck from 'bottleneck'
const limiter = new BottleNeck({
maxConcurrent: 1,
minTime: 333
})
export const Request = (items, setItems) => {
const getData = () => {
const options = 'newstories'
const API_URL = `https://hacker-news.firebaseio.com/v0/${options}.json?print=pretty`;
return new Promise((resolve, reject) => {
return resolve(axios.get(API_URL))
})
}
const getIdFromData = (dataId) => {
const API_URL = `https://hacker-news.firebaseio.com/v0/item/${dataId}.json?print=pretty`;
return new Promise((resolve, reject) => {
return resolve(axios.get(API_URL))
})
}
const runAsyncFunctions = async () => {
const {data} = await getData()
Promise.all(
data.map(async (d) => {
const {data} = await limiter.schedule(() => getIdFromData(d))
//****************** issue here ************************//
setItems([...items, data]);
})
)
}
runAsyncFunctions()
}
just in case you want to see the app.js file for reference
import React, { useState, useEffect } from 'react';
import './App.css';
import { SearchBar } from './search-bar';
import { Results } from './results';
import { Request } from './helper/request'
function App() {
const [input, setInput] = useState('');
const [items, setItems] = useState([]);
const handleChange = val => {
setInput(val)
}
// console.log(input)
// console.log(results)
// API calls
// call useEffect here, calls Request(), put results in useEffect
useEffect(() => {
Request(items, setItems)
}, [])
return (
<div className="App">
<SearchBar handleChange={handleChange}/>
<Results items={items} />
</div>
);
}
export default App;
At your Promise.all return each data, after you can chain with then that passes an array with all resolved data. This way you only need to call it once setItems:
Promise.all(
data.map(async (d) => {
const { data } = await limiter.schedule(() => getIdFromData(d));
return data;
})
).then((dataResults) => setItems((results) => [...results, ...dataResults]));

Resources