Cannot return position in from navigation.geolocation.getCurrentPosition() in react-native - react-native

I am trying to get the geolocation after an image has been taken in react-native. A user captures an image and the image along with the geolocation is stored in an object and sent via a http request to the server.
The function to save get the geolocation works fine bur I am unable to return the geolocation to be stored in the object for http transfer. I get an undefined.
console.log('getCoordinates run')
await navigator.geolocation.getCurrentPosition(
position => {
let coordinates = `${position.coords.longitude},
${position.coords.latitude}`
return coordinates
},
error => Alert.alert(error.message),
{ enableHighAccuracy: false, timeout: 20000, maximumAge: 1000 }
)
}
captureImage = async () => {
if (this.camera) {
const options = { quality: 0.5, base64: true };
const data = await this.camera.takePictureAsync(options);
console.log(data);
let postData = {
user: 1,
coordinates: this.getCoordinates(),
image: `data:image/jpeg;base64${data.base64}`,
}
console.log(postData)
axios.post('https://localhost:5000/api/posts', postData)
.then(post => res.json(post))
.catch(err => console.log(err))
}
}
Expected results is that when the captureImage function runs the getCoordinates function withing the postData object returns the current geolocation before that data is transferred to the server.

How geolocation.getCurrentPosition function works here is that it sets a callback to send data once it acquire user's location. It takes time to acquire and send relevant data. That's why we use callbacks or promises. But in your code, you just call the function and without waiting for its response, just do the API call.
I assume you have used Async function to do this. But if I were you, I'd try to use Promises here to resolve this issue. Simple example would be,
captureImage = async () => {
if (this.camera) {
// ... do your camera tasks
}
this.sendImageData(data); // data is what you got from camera.
}
getGeoInfo = () => {
return new Promise((resolve, reject) => {
navigator.geolocation.getCurrentPosition(
position => {
let coordinates = `${position.coords.longitude},
${position.coords.latitude}`
resolve(coordinates);
},
error => reject(error),
{ enableHighAccuracy: false, timeout: 20000, maximumAge: 1000 }
)
})
}
sendImageData = (cameraData) => {
let coordinates = '';
getGeoInfo.then(crdnts => coordinates = crdnts );
// now coordinates have all relevant data you wished.
const data = { //... make the object as you want }
// do the API call using axios as you've already done.
}

Related

API resolved without sending a response - Next.js

I've this code to get nearby places and nearby beaches from a point, with Google maps. This is called from a Next.js component, via the useSWR hook.
All the data is returned correctly, but before first Axios call (const fetchNearbyPlaces = async (urlWithToken = null) => {...), I'm receiving this error in the console:
API resolved without sending a response for /api/google/places/33.807501/-78.70039, this may result in stalled requests.
I can't figure out what the error is, although there may be several because I'm a novice. I appreciate any suggestion.
const axios = require("axios");
const GetNearbyPlaces = async (req, res) => {
const {
latitude,
longitude,
} = req.query;
const radius = 50000;
const types = [
"airport",
"tourist_attraction",
"amusement_park",
"aquarium",
"art_gallery",
"bar",
"museum",
"night_club",
"cafe",
"restaurant",
"shopping_mall",
"store",
"spa",
];
function checkFunc(arr, val) {
return arr.some(arrVal => val === arrVal);
}
const url = `https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=${latitude}%2C${longitude}&radius=${radius}&key=${process.env.CW_GOOGLE_MAPS_API_KEY}`;
const beachesUrl = `https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=${latitude}%2C${longitude}&radius=${radius}&type=natural_feature&key=${process.env.CW_GOOGLE_MAPS_API_KEY}`;
try {
let results = [];
let beaches = [];
const fetchNearbyBeaches = async (urlWithToken = null) => {
await axios.get(urlWithToken ? urlWithToken : beachesUrl).then(data => {
beaches = [...beaches, ...data.data.results];
if (data?.data?.next_page_token) {
const newUrl = `https://maps.googleapis.com/maps/api/place/nearbysearch/json?key=${process.env.CW_GOOGLE_MAPS_API_KEY}&pagetoken=${data.data.next_page_token}`;
setTimeout(() => {
fetchNearbyBeaches(newUrl);
}, 2000);
} else {
beaches.length > 5 && beaches.splice(5);
results.length > 5 && results.splice(5);
const finalResults = [...beaches, ...results];
finalResults.length > 10 && finalResults.splice(10);
return res.status(200).json({
data: {
results: finalResults,
},
success: true,
});
}
});
};
const fetchNearbyPlaces = async (urlWithToken = null) => {
await axios.get(urlWithToken ? urlWithToken : url).then(data => {
results = [...results, ...data.data.results];
if (data?.data?.next_page_token) {
const newUrl = `https://maps.googleapis.com/maps/api/place/nearbysearch/json?key=${process.env.CW_GOOGLE_MAPS_API_KEY}&pagetoken=${data.data.next_page_token}`;
setTimeout(() => {
fetchNearbyPlaces(newUrl);
}, 2000);
} else {
const dirtyResultsWithDuplicates = [];
results.map(result => {
return types.map(type => {
if (checkFunc(result.types, type) && !result.types.includes("lodging")) {
dirtyResultsWithDuplicates.push(result);
}
});
});
const set = new Set(dirtyResultsWithDuplicates);
const filtered = Array.from(set);
results = filtered.length > 10 ? filtered.splice(10) : filtered;
return fetchNearbyBeaches();
}
});
};
fetchNearbyPlaces();
} catch (err) {
res.status(500).json({
message: err.message,
statusCode: 500,
});
}
};
export default GetNearbyPlaces;
The problem is with the backend application not the frontend component.
Nextjs expects a response to have been sent when the api handler function exits. If for example you have a databaseCall.then(sendResponse) in your api handler function what happens is that the handler function exits before the database returns.
Now this is not a problem if the database does return after that and sends the response, but it is if for example the database has an error. Because the handler function exits without a response already being sent Nextjs can't be sure that at that point there isn't a stalled request.
One way to fix this is by await-ing the db call(or whatever other async function you call) thereby preventing the handler function from exiting before some kind of response has been send.
The solution was added this object to mi API code.
export const config = {
api: {
externalResolver: true,
},
};
Documentation: https://nextjs.org/docs/api-routes/request-helpers

React Native UseEffect function is not working according to order

I want to get user's current location and set it into AsyncStorage a array. I will do it in the useEffect hook. But the problem is my functions are not working that according to given order. Here are my code
useEffect(() => {
getUserLocation();
setUserLocation();
check();
}, []);
/*Get User's Currunt Location*/
const getUserLocation = () => {
GetLocation.getCurrentPosition({
enableHighAccuracy: true,
timeout: 15000,
})
.then((location) => {
var lt = location.latitude;
var lg = location.longitude;
setlatitude(lt);
setlongitude(lg);
console.log("getUserLocation", lt, lg);
})
.catch((error) => {
const { code, message } = error;
console.warn(code, message);
});
};
/*Set User's Currunt Location to AsyncStorage*/
const setUserLocation = async () => {
try {
await AsyncStorage.setItem("user_location", JSON.stringify(userLocation));
console.log("setUserLocation", userLocation);
} catch (error) {
console.log("error setting user location");
}
};
const check = () => {
AsyncStorage.getItem("user_location", (err, result) => {
if (result !== null) {
console.log("check", result);
setlatitude(result.latitude);
setlongitude(result.longitude);
} else {
console.log("Data Not Found");
}
});
};
Whenever you use .then you are scheduling your code to run at some point in the future, when the promise has completed. So setUserLocation runs before the then of getUserLocation.
Also, it looks like your getUserLocation set react state, which won't be available until the next render. We use effects to manage this.
// Get the location on mount
useEffect(getUserLocation, []);
// Whenever the location updates, set it into storage
useEffect(() => setUserLocation().then(check), [latitude, longitude]);

React Native Save captured images and video to custom folder on my device

I have tried to research for the right answer for me on saving captured images or videos to custom folder on device but have not seen a suitable answers. I have been able to save to my DCIM, but I don't want to save them there, I want to create a custom folder to save my captured images or video from my app. I am new to react native and this is my learning process...
takePicture = async () => {
if (this.camera) {
if (Platform.OS === 'android') {
await this.checkAndroidPermission();
}
const options = { quality: 1 };
const data = await this.camera.takePictureAsync(options);
//save photo
CameraRoll.save(data.uri, 'photo').then(onfulfilled => {
ToastAndroid.show(`VidApp Photos: ${onfulfilled}`, ToastAndroid.SHORT);
}).catch(error => {
ToastAndroid.show(`${error.message}`, ToastAndroid.SHORT);
});
}
};
recordVideo = async () => {
if (this.camera) {
if (!this.state.recording)
this.startRecording();
else this.stopRecording();
}
}
startRecording = async () => {
this.setState({ recording: true });
this.countRecordTime = setInterval(() => this.setState({ seconds: this.state.seconds + 1 }), 1000);
const cameraConfig = { maxDuration: this.state.maxDuration };
const data = await this.camera.recordAsync(cameraConfig);
this.setState({ recording: false });
CameraRoll.save(data.uri, 'video').then(onfulfilled => {
ToastAndroid.show(`VidApp Videos: ${onfulfilled}`, ToastAndroid.SHORT)
}).catch(error => ToastAndroid.show(`${error.message}`, ToastAndroid.SHORT));
}
stopRecording = () => {
this.camera.stopRecording();
clearInterval(this.countRecordTime);
this.setState({ seconds: 0 });
You have to use the album parameter of CameraRoll.save
CameraRoll.save(data.uri, {type:'photo',album:'CustomFolder'});
from the docs
It allows to specify a particular album you want to store the asset to
when the param album is provided. On Android, if no album is provided,
DCIM directory is used, otherwise PICTURE or MOVIES directory is used
depending on the type provided.

Routing back with in a time frame

I am new to coding.In my app after the submission of a form i will get a message that i have submitted successfully or is it an error.After getting the message i want to revert back the user to my previous page with in 5 seconds.while using $router.push getting 'can not read property of undefine push'If some one knows please...
this the scrip to call
enter code here
methods: {
submitForm() {
formService.hospital({
firstName: this.firstName,
,
date: new Date(this.date),
time: this.time
}) .then(response => {
response.data;
console.log(response);
this.isSuccessMessage = true;
this.isErrorMessage = false;
this.$store.dispatch('addPickupAssistanceMessage');
setTimeout(function(){ this.$router.push('/dashboard'); 5000 });
}).catch(error => {
console.log("Error reported from endpoints :", JSON.stringify(error.response));
this.isErrorMessage = true;
this.$store.dispatch('addErrorMessage')
return (this.errorMessage = JSON.stringify(
error.response.data.errorMessage
))
});
},
The problem is inside the handler function passed to setTimeout, which uses this.$router, which is a property of the instance. The problem can be solved with arrow functions.
setTimeout(() => { this.$router.push('/dashboard'); }, 5000);

Doing a Timeout Error with Fetch - React Native

I have a user login function that is working. But, I want to incorporate a time out error for the fetch. Is there a way to set up a timer for 5 seconds or so that would stop trying to fetch after such a time? Otherwise, I just get a red screen after a while saying network error.
_userLogin() {
var value = this.refs.form.getValue();
if (value) {
// if validation fails, value will be null
if (!this.validateEmail(value.email)) {
// eslint-disable-next-line no-undef
Alert.alert('Enter a valid email');
} else {
fetch('http://51.64.34.134:5000/api/login', {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
timeout: 5000,
body: JSON.stringify({
username: value.email,
password: value.password,
}),
})
.then((response) => response.json())
.then((responseData) => {
if (responseData.status == 'success') {
this._onValueChange(STORAGE_KEY, responseData.data.token);
Alert.alert('Login Success!');
this.props.navigator.push({name: 'StartScreen'});
} else if (responseData.status == 'error') {
Alert.alert('Login Error', responseData.message);
}
})
.done();
}
}
}
I have made a ES6 function that wraps ES fetch into a promise, here it is:
export async function fetchWithTimeout(url, options, timeout = 5000) {
return Promise.race([
fetch(url, options),
new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), timeout))
]);
}
Here is how to use it:
const requestInfo = {
method,
headers,
body,
};
const url = 'http://yoururl.edu.br'
let data = await fetchWithTimeout(url, requestInfo, 3000);
// Wrapper function for fetch
const fetchSomething = async () => {
let controller = new AbortController()
setTimeout(() => controller.abort(), 3000); // abort after 3 seconds
const resp = await fetch('some url', {signal: controller.signal});
const json = await resp.json();
if (!resp.ok) {
throw new Error(`HTTP error! status: ${resp.status}`);
}
return json;
}
// usage
try {
let jsonResp = await fetchSomthing();
console.log(jsonResp);
} catch (error) {
if (error.name === 'AbortError') {
console.log('Network Error');
} else {
console.log(error.message);
}
}
I think using AbortController is the recommended way to abort a fetch call. The code snippet above handles the following scenarios:
If network is good but HTTP returns an error status, the message "HTTP error! ..." will be logged.
If network is down, setTimeout would trigger the AbortController to abort fetch after three seconds. The message "Network Error" will be logged.
If network is good and HTTP response is good, the response JSON will be logged.
The documentation for using AbortController to abort fetch is here.
There is no standard way of handling this as a timeout option isn't defined in the official spec yet. There is an abort defined which you can use in conjunction with your own timeout and Promises. For example as seen here and here. I've copied the example code, but haven't tested it myself yet.
// Rough implementation. Untested.
function timeout(ms, promise) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
reject(new Error("timeout"))
}, ms)
promise.then(resolve, reject)
})
}
timeout(1000, fetch('/hello')).then(function(response) {
// process response
}).catch(function(error) {
// might be a timeout error
})
Another option would be to modify the fetch.js module yourself to add a timeout that calls abort as seen here.
This is what I did to go around it:
(This is the "generic" function I use to make all calls on my app)
I created a timeout function, that will be triggered unless it is cleared before, then I clear this timeout on server response
const doFetch = (url, callback, data) => {
//... creating config obj here (not relevant for this answer)
var wasServerTimeout = false;
var timeout = setTimeout(() => {
wasServerTimeout = true;
alert('Time Out');
}, 3000);
fetch(HOST + url, config)
.then((response) => {
timeout && clearTimeout(timeout); //If everything is ok, clear the timeout
if (!wasServerTimeout) {
return response.json();
}
})
.then((response) => {
callback && callback(response.data || response);
})
.catch((err) => {
//If something goes wrong, clear the timeout
timeout && clearTimeout(timeout);
if (!wasServerTimeout) {
//Error logic here
}
});
};
I solved this problem by using a race between 2 promises, written as a wrapper around fetch. In my case I expect the request to return json so also added that. Maybe there is a better solution, but this works correctly for me!
The wrapper returns a promise which will resolve as long as there are no code errors.
You can check the result.status for 'success' and read json data from result.data. In case of error you can read the exact error in result.data, and display it or log it somewhere. This way you always know what went wrong!
var yourFetchWrapperFunction = function (
method,
url,
headers,
body,
timeout = 5000,
) {
var timeoutPromise = new Promise(function (resolve, reject) {
setTimeout(resolve, timeout, {
status: 'error',
code: 666,
data:
'Verbinding met de cloud kon niet tot stand gebracht worden: Timeout.',
});
});
return Promise.race([
timeoutPromise,
fetch(connectionType + '://' + url, {
method: method,
headers: headers,
body: body,
}),
])
.then(
(result) => {
var Status = result.status;
return result
.json()
.then(
function (data) {
if (Status === 200 || Status === 0) {
return {status: 'success', code: Status, data: data};
} else {
return {
status: 'error',
code: Status,
data: 'Error (' + data.status_code + '): ' + data.message,
};
}
},
function (response) {
return {
status: 'error',
code: Status,
data: 'json promise failed' + response,
};
},
)
.catch((error) => {
return {status: 'error', code: 666, data: 'no json response'};
});
},
function (error) {
return {status: 'error', code: 666, data: 'connection timed out'};
},
)
.catch((error) => {
return {status: 'error', code: 666, data: 'connection timed out'};
});
};
let controller = new AbortController()
setTimeout( () => {
controller.abort()
}, 10000); // 10,000 means 10 seconds
return fetch(url, {
method: 'POST',
headers: headers,
body: JSON.stringify(param),
signal: controller.signal
})
I may be late but i made a code which is 100% working to timeout an API request using fetch.
fetch_timeout(url, options) {
let timeout = 1000;
let timeout_err = {
ok: false,
status: 408,
};
return new Promise(function (resolve, reject) {
fetch(url, options)
.then(resolve, reject)
.catch(() => {
alert('timeout.');
});
setTimeout(reject.bind(null, timeout_err), timeout);
});
}
You just need to pass the api-endpoint to the url and body to the options parameter.