I am not able to stop the api call. what should be the right approach to handle this situation.
const successCallback = (position) => {
console.log("in success");
setCoord({ ...coord, latitude: position.coords.latitude.toFixed(4), longitude: position.coords.longitude.toFixed(4) })
}
const failCallback = () => {
alert("Give Location Permission for currect Location.")
}
if (window.navigator.geolocation) {
window.navigator.geolocation.getCurrentPosition(successCallback, failCallback)
} else {
alert("Geolocation is not supported by this browser.")
}
const url3 = `http://api.openweathermap.org/data/2.5/weather?lat=${coord.latitude}&lon=${coord.longitude}&appid=MYKEY`
const fetchWeather = async () => {
const responce = await Axios.get(url3)
console.log(responce);
}
useEffect(() => {
fetchWeather()
}, [coord])
Add options to throttle how often successCallback is callded
const options = {
enableHighAccuracy: true,
timeout: 10000,
maximumAge: 20000
};
window.navigator.geolocation.getCurrentPosition(successCallback, failCallback, options)
or if you want it to be called only once, use
const options = {
timeout: 0
};
Related
I am hosting a react app in aws amplify using the aws-serverless version of express as the REST API, which sits inside of a lambda function. A big problem that I am facing is that asynchronous jobs in aws-serverless express cause the lambda function to complete before the promises resolve. Leaving me with no data and no error handling. This caused me to bring a lot of the asynchronous work to the front end of the application.
The problem here is that I need to bring a large amount of data into state. Right now, I am using a delay workaround (shown below) but instead need a programatic way to make sure state is finished updating before being used in the second useEffect hook (dependent on odds & failedTries props) instead of using the delay functionality.
Any help would be greatly appreciated.
const App = ({ signOut }) => {
const [odds, setOdds] = useState([]);
const [updateTime,setUpdateTime] = useState(0);
const [failedTries,setFailedTries] = useState(0);
useEffect(() => {
const setNflOdds = async () => {
let response = await updateNflOdds();
let data = response;
setOdds(data);
};
setNflOdds();
setUpdateTime(1);
const interval = setInterval(() => {
setNflOdds();
setUpdateTime(updateTime => updateTime +1);
}, 100000);
return () => clearInterval(interval);
}, []);
useEffect(() => {
const s3Push = (() => {
if(!odds.length) {
setFailedTries(failedTries => failedTries + 1);
} else {
const delay = ms => new Promise(res => setTimeout(res, ms));
const nflOddsRefDelay = async() => {
*//This is the current workaround, wait ten seconds before pushing odds state up to the s3 bucket*
await delay(10000);
oddsS3Helper(odds);
};
nflOddsRefDelay()
}
});
s3Push();
}, [odds, failedTries]);
With the above indicated delay workaround this works for my use case (13k records inside of the array) but the data size is highly variable and I want to figure out a way that no matter the data size it brings the entire call up to the s3 bucket.
below is the content of the functions being called in the useEffect hook
const pushToS3 = async ( file, key ) => {
const creds = await Auth.currentCredentials()
const REGION = {region};
const s3Client = new S3Client({
credentials: Auth.essentialCredentials(creds),
region: REGION
});
const params = {
Bucket: {s3 bucket name}
Key: key,
Body: file,
};
s3Client.send(new PutObjectCommand(params));
console.log("file is sent");
};
const oddsS3Helper = (async (odds) => {
console.log("inside s3 helper: ",odds);
let csv = '';
let headers = Object.keys(odds[0]).join(',');
let values = odds.map(odd => Object.values(odd).join(',')).join('\n');
csv += headers + '\n' + values;
const buffedFile = csv;
const key = 'nflprops.csv'
const delay = ms => new Promise(res => setTimeout(res, ms));
const propRefDelay = async() => {
await delay(5000);
await postNflOdds();
};
pushToS3( buffedFile, key );
await propRefDelay();
});
async function getNflGames() {
const apiName = {name of serverless API inside of lambda};
const path = {path name};
const init = {
headers: {} // OPTIONAL
};
const data = await API.get(apiName, path, init);
return data;
};
async function getNflOdds(gameId) {
const apiName = {name of serverless API inside of lambda};
const path = {path name};
const init = {
headers: {}, // OPTIONAL
body: { gameId }
};
const data = await API.post(apiName, path, init);
return data;
};
async function updateNflOdds() {
const ojNflGames = await getNflGames();
const nflGameProps = [];
const nflOddsPush = ( async () => {
try {
await ojNflGames.data.map( async (game) => {
const ojNflOdds = await getNflOdds(game.id)
await ojNflOdds.data[0].odds.map((line) => {
nflGameProps.push(
{
gameId: game.id,
oddsId: line.id,
sports_book_name: line.sports_book_name,
name: line.name,
price: line.price,
checked_date: line.checked_date,
bet_points: line.bet_points,
is_main: line.is_main,
is_live: line.is_live,
market_name: line.market_name,
home_rotation_number: line.home_rotation_number,
away_rotation_number: line.away_rotation_number,
deep_link_url: line.deep_link_url,
player_id: line.player_id,
}
);
});
});
} catch (err) {
console.log("there was an error", err);
}
});
try {
await nflOddsPush();
} catch(err) {
console.log("odds push errored: ", err);
}
console.log("inside of updateNflOdds function: ",nflGameProps);
return nflGameProps;
};
i create custom hook for use useQuery() :
export const useRequest = (
{
path = "",
params = {},
body = {},
key = "",
options= {},
method = "get"
}
) => {
// async function for get API:
const callApi = async () => {
const { data: { response } } = await axios[method](baseUrl._serviceUrl + path,
{
params,
...body
});
return response;
}
const query = useQuery(key, callApi, {
refetchOnWindowFocus: false,
...options
});
return { ...query, isLoading: query.isLoading && query.fetchStatus !== "idle" }
}
To use the useMutation hook without using the separate function, I changed my useRequest() as follows:
export const useRequest = (
{
path = "",
params = {},
body = {},
key = "",
options= {},
method = "get",
mutation = false
}
) => {
// async function for get API:
const callApi = async () => {
const { data: { response } } = await axios[method](baseUrl._serviceUrl + path,
{
params,
...body
});
return response;
}
if (mutation) {
const callMutationApi = async (data) => {
const {params, body} = data;
const { data: { response } } = await axios.post(baseUrl._serviceUrl + path,
{
params,
...body
});
return response;
}
return useMutation(callMutationApi, options);
}
const query = useQuery(key, callApi, {
refetchOnWindowFocus: false,
...options
});
return { ...query, isLoading: query.isLoading && query.fetchStatus !== "idle" }
}
But I get the following error:
React Hook "useMutation" is called conditionally. React Hooks must be called in the exact same order in every component render. Did you accidentally call a React Hook after an early return?
What changes should I make in the useRequest() function? Thank you for your guidance.
///// UPDATE /////
According to the answer of dear #kapobajza...
I changed the code as follows and it worked:
export const useRequest = (
{
path = "",
params = {},
body = {},
key = "",
options= {},
method = "get",
mutation = false
}
) => {
// async function for get API:
const callApi = async () => {
const { data: { response } } = await axios[method](baseUrl._serviceUrl + path,
{
params,
...body
});
return response;
}
const callMutationApi = async (data) => {
const {params, body} = data;
const { data: { response } } = await axios.post(baseUrl._serviceUrl + path,
{
params,
...body
});
return response;
}
// Instead of returning here, just save the result in a variable
const useMutationResult = useMutation(callMutationApi, options);
const query = useQuery(key, callApi, {
refetchOnWindowFocus: false,
enabled: (!mutation && options?.enabled),
...options
});
// If mutation is defined, return that result
if (mutation) {
return useMutationResult;
}
return { ...query, isLoading: query.isLoading && query.fetchStatus !== "idle" }
}
I added the option part of the following code to useQuery().. This will prevent useQuery() from running:
{
refetchOnWindowFocus: false,
enabled: (!mutation && options?.enabled),
...options
}
Please tell me if this solution is correct?
Unfortunately you cannot call React hooks conditionally.
One thing you'll find out early adopting react is that you cannot have conditional hooks. This is because every hook is initially added into a list that is reviewed on every render cycle, so if the hooks don't add up, there is something amiss and any linter set up correctly will warn you.
Taken from this article
But the thing you could do is to return the result of the useMutation hook if mutation is defined:
export const useRequest = (
{
path = "",
params = {},
body = {},
key = "",
options= {},
method = "get",
mutation = false
}
) => {
// async function for get API:
const callApi = async () => {
const { data: { response } } = await axios[method](baseUrl._serviceUrl + path,
{
params,
...body
});
return response;
}
const callMutationApi = async (data) => {
const {params, body} = data;
const { data: { response } } = await axios.post(baseUrl._serviceUrl + path,
{
params,
...body
});
return response;
}
// Instead of returning here, just save the result in a variable
const useMutationResult = useMutation(callMutationApi, options);
const query = useQuery(key, callApi, {
refetchOnWindowFocus: false,
...options
});
// If mutation is defined, return that result
if (mutation) {
return useMutationResult;
}
return { ...query, isLoading: query.isLoading && query.fetchStatus !== "idle" }
}
My .net core react web application works fine, except that when I try to publish it gives me the following error:
Occurred while linting C:\.....Fetch.js: 79
Rule: "react-hooks/exhaustive-deps"
This is my code:
const populateTable1Data = async () => {
var response = await axios.get(apiurl + { params: { id: props.id1 } });
var data = await response.data;
setTable1Data(data);
}
const populateTable2Data = async () => {
var response = await axios.get(apiurl + { params: { id: props.id2 } });
var data = await response.data;
setTable2Data(data);
setLoading(false);
}
useEffect(() => {
const load = async () => {
await populateTable1Data();
await populateTable2Data();
setLoading(false)
}
load()
}, []);
Problem is that I have a very similar useEffect inside another component which doesn't give errors though:
const populateTableData = async () => {
const response = await axios.get(apiurl + key);
const data = await response.data;
setTableData(data);
setLoading(false);
}
useEffect(() => {
populateTableData();
}, [])
If anyone has the same problem, I solved by doing this:
const populateTable1Data = async (dataProps) => {
var response = await axios.get(apiurl + { params: { id: dataProps.id1 } });
var data = await response.data;
setTable1Data(data);
}
const populateTable2Data = async (dataProps) => {
var response = await axios.get(apiurl + { params: { id: dataProps.id2 } });
var data = await response.data;
setTable2Data(data);
setLoading(false);
}
useEffect(() => {
const load = async () => {
await populateTable1Data(props);
await populateTable2Data(props);
setLoading(false)
}
load()
}, [props]);
I essentially passed the props on the function call, I don't know why does it have to be this way, I'll leave the answer here in case anyone else needs it while waiting for someone to be kind enought to explain the reason for this.
I am new in react native and try to call two api from useEffect but it give me this error every time React Hook useEffect has a missing dependency: 'getAllPost'. Either include it or remove the dependency array.
Here is my code
export default function Home({navigation}) {
const [arrCat, setArrCat] = useState([]);
const [arrPost, setArrPost] = useState([]);
const [isLoading, setLoding] = useState(false);
function getAllCategory() {
setLoding(true);
let apiResponse = ApiManager.GET('category/all', [], 'GET');
apiResponse
.then(response => {
let responseJson = response[1];
let status = response[0];
setLoding(false);
let message =
responseJson.message != null
? response.message
: 'Something went wrong';
if (status === 200) {
setArrCat([...responseJson.data]);
getAllPost();
}
setTimeout(function() {
if (message != null) {
Toast.showWithGravity(message, Toast.LONG, Toast.BOTTOM);
}
}, 120);
})
.catch(error => {
console.error(error);
Toast.showWithGravity(error, Toast.LONG, Toast.BOTTOM);
setTimeout(function() {
setLoding(false);
}, 60);
});
}
function getAllPost() {
GetLocation.getCurrentPosition({
enableHighAccuracy: true,
timeout: 15000,
})
.then(location => {
console.log(location);
const dictData = {
lat: '-37.81400200-33.865143', //location.latitude,
lang: '144.9546943', //location.longitude,
record_count: '0',
};
console.log(dictData);
let apiResponse = ApiManager.POST(
'post/getRecommendedPost',
dictData,
'POST',
);
apiResponse
.then(response => {
let responseJson = response[1];
let status = response[0];
if (status === 200) {
console.log(responseJson);
setArrPost(oldValue => [...oldValue, ...responseJson.data]);
console.log(arrPost);
} else {
// console.error(responseJson);
Toast.showWithGravity(
responseJson.message,
Toast.LONG,
Toast.BOTTOM,
);
}
})
.catch(error => {
// console.error(error);
Toast.showWithGravity(error.message, Toast.LONG, Toast.BOTTOM);
// setTimeout(function() {
// setLoding(false);
// }, 60);
});
})
.catch(error => {
// const {code, message} = error;
// console.warn(code, message);
Toast.showWithGravity(error.message, Toast.LONG, Toast.BOTTOM);
});
}
useEffect(() => {
console.log('Home screen mounted');
getAllCategory();
// getAllPost();
}, []);
return ( ....)
}
The following code uploads a file no problem and responds successfully or failing as expected, however, I cannot figure out how to dispatch my uploadFileProgress action from the onUploadProgress event. I can console.log the progress / percentage and when I try to wrap the dispatch in an IIFE, I trigger a dispatch is not a function error. Hopefully this is a small issue I'm missing. Thanks in advance!
export function uploadFile(values, callback = () => {}) {
const uploadFileData = new FormData();
uploadFileData.append('fileName', values.fileName);
uploadFileData.append('file', values.file);
uploadFileData.append('file', {
filename: values.filename,
contentType: values.contentType,
});
const uploadProgress = {
onUploadProgress: (ProgressEvent) => {
let progressData = 0;
const totalLength = ProgressEvent.lengthComputable ? ProgressEvent.total : ProgressEvent.target.getResponseHeader('content-length') || ProgressEvent.target.getResponseHeader('x-decompressed-content-length');
if (totalLength !== null) {
progressData = Math.round((ProgressEvent.loaded * 100) / totalLength);
}
return function action(dispatch) {
dispatch(uploadFileUpload(progressData));
};
},
};
const configPlusProgress = Object.assign(uploadProgress, config);
const request = () => axios.post(myURL, uploadFileData, configPlusProgress);
return function action(dispatch) {
dispatch(uploadFileLoading(true));
return request()
.then((response) => {
if (response.status !== 201) {
dispatch(uploadFileFail());
throw Error(response.statusText);
}
dispatch(uploadFileLoading(false));
return response;
})
.then(response => dispatch(uploadFileSuccess(response)))
.then(() => callback())
.catch(err => dispatch(uploadFileFail(err)));
};
}
move your request config inside returned function (where dispatch function will be accessible):
export function uploadFile(values, callback = () => {}) {
const uploadFileData = new FormData();
uploadFileData.append('fileName', values.fileName);
uploadFileData.append('file', values.file);
uploadFileData.append('file', {
filename: values.filename,
contentType: values.contentType,
});
return function action(dispatch) {
const uploadProgress = {
onUploadProgress: (ProgressEvent) => {
let progressData = 0;
const totalLength = ProgressEvent.lengthComputable ? ProgressEvent.total : ProgressEvent.target.getResponseHeader('content-length') || ProgressEvent.target.getResponseHeader('x-decompressed-content-length');
if (totalLength !== null) {
progressData = Math.round((ProgressEvent.loaded * 100) / totalLength);
}
dispatch(uploadFileUpload(progressData));
},
};
const configPlusProgress = Object.assign(uploadProgress, config);
const request = () => axios.post(myURL, uploadFileData, configPlusProgress);
dispatch(uploadFileLoading(true));
return request()
.then((response) => {
if (response.status !== 201) {
dispatch(uploadFileFail());
throw Error(response.statusText);
}
dispatch(uploadFileLoading(false));
return response;
})
.then(response => dispatch(uploadFileSuccess(response)))
.then(() => callback())
.catch(err => dispatch(uploadFileFail(err)));
};
}
Also onUploadProgress should just dipatch upload progress event.
I can't quite fix your code but here is a basic function with redux-thunk doing async stuff and using actions.
const doSomeAsyncStuff = () =>
async ( dispatch ) => {
try {
const response = await someAsyncStuff();
return dispatch( someSuccessAction( response.data );
} catch ( error ) {
return dispatch( someFailureAction( err );
}
}
Of course redux-thunk must be added as a middleware.
why are you returning a function from onUploadProgress function
return function action(dispatch) {
dispatch(uploadFileUpload(progressData));
};
Instead of that you can just
dispatch(uploadFileUpload(progressData));