React Native UseEffect function is not working according to order - react-native

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]);

Related

Setting data from firebase with useState is returning undefined

I am trying to set data for verification purposes. I do set the data then get undefined which is disturbing to me, I've tried to parse it in different shapes, I've used useCallback hook and without any real benefit
const getUserPhone = useCallback(async () => {
console.log('user phone is requested');
await firebase
.database()
.ref(`users/${login.uid}`)
.once(
'value',
async (data) => {
if (data.exists()) {
console.log('found');
await setUserData(data.toJSON());
console.log('data has been set');
} else {
Alert.alert('User not found');
return;
}
},
// I've tried .get() from firebase and
//.then(async (data: IUser) => {await setUserData(data.toJSON()})
// It does the same.
)
.catch((error) => {
return console.error(error);
});
}, [login.uid]);
const handleVerification = useCallback(async () => {
if (alreadyRequested) {
return;
}
await getUserPhone();
try {
console.log(userData); // undefined
if (!userData?.phoneNumber) {
console.log('no phone number is here');
return;
}
...
} catch ...
}, [alreadyRequested, getUserData, userData?.phoneNumber])
Output:
user phone is requested
found
data has been set
undefined
no phone number is here

React Hook does not set on first API call

So I am sure I am messing something up, but I am not super skilled at API.
So I am trying to make an API call to check if the user exists, if user exists then move about business, if not then do other stuff.
So my first call gets the data, and the user DOES exist, the hook is setting to true, however in my log it fails and the next API is ran. However if I do it a 2nd time, it is true...
What am I doing wrong.
const handleSubmit = async () => {
const data = await axios
.get(`URL`, {
})
.then((resp) => {
if (resp.data.user.name) {
setCheckUser(true);
console.log(resp.data.user.name);
}
return data;
})
.catch((err) => {
// Handle Error Here
console.error(err);
});
console.log(checkUser);
if (!checkUser) {
console.log('No User Found');
//Do Stuff//
}
};
I think the problem here is that setCheckUser(true) is an async operation, so there is no guarantee that the checkUser variable will turn to true right away.
Maybe you can solve this by using a useEffect block like this
//somewhere on the top of your file, below your useState statements
useEffect(()=> {
if (!checkUser) {
console.log('No User Found');
//Do Stuff//
}
}, [checkUser])
const handleSubmit = async () => {
const data = await axios
.get(`URL`, {
})
.then((resp) => {
if (resp.data.user.name) {
setCheckUser(true);
console.log(resp.data.user.name);
}
return data;
})
.catch((err) => {
// Handle Error Here
console.error(err);
});
};

React Native async await dispatch store

So I want to make a login feature, in here server will validate first if the username or password is correct or not.
I'm using store, react - redux.
Here is my code when login button pressed
const [statusLogin,setStatusLogin] = useState(null)
let loginInfo = []
function loginButton(){
(async () => {
loginInfo = {username:username,password:password}
const { status } = await dispatch(getUser(loginInfo))
if (status==1){
console.log(status,'in status if 1')
setStatusLogin('granted')
}else{
console.log(status,'in status if else')
setStatusLogin(null)
}
})();
}
Here is my store that suppose to return value 1 or else
if it returned value 1 geb statusLogin will changed as granted
export function getUser(body){
return dispatch =>{
if (!body){
setTimeout(() => {
console.log('no username/pass')
}, 2000);
}else{
setTimeout(() => {
console.log('username/pass validated returning with value 1')
}, 2000);
}
}
}
help me please
This might help
...
function loginButton() {
(async () => {
loginInfo = { username: username, password: password };
await dispatch(getUser(loginInfo, callback));
})();
}
function callback = (status) => {
if (status == 1) {
console.log(status, "in status if 1");
setStatusLogin("granted");
} else {
console.log(status, "in status if else");
setStatusLogin(null);
}
};
reducer.js
export function getUser(body, callback){
return dispatch =>{
if (!body){
setTimeout(() => {
console.log('no username/pass');
callback(0);
}, 2000);
}else{
setTimeout(() => {
console.log('username/pass validated returning with value 1')
callback(1);
}, 2000);
}
}
}
you can use like:
usEffect(( )=>{
if(statusLogin) getUser()
},[statusLogin])
Another thing, your function should not passed callback in. Instead of using callback to change the state, you can use dispatch to modify the reducer.

Can't perform a React state update on an unmounted component. useEffect Hook

I seem to be missing something subtle about avoiding memory leaks. I have read a few posts on how to avoid this with async functions and have tried a few things. All seem to fail. Could someone point out what I'm doing wrong.
useEffect(() => {
let ignore = false;
if (Platform.OS === "android" && !Constants.isDevice) {
errorMessage("Oops, this will not work on Sketch in an Android emulator. Try it on your device!");
} else {
// function to get location, weather and aurora info
const getDataAsync = async () => {
let { status } = await Permissions.askAsync(Permissions.LOCATION);
if (status !== "granted") {
setErrorMessage("Permission to access location was denied");
}
if (!ignore) {
let location = await Location.getCurrentPositionAsync({});
// do stuff with the location data, putting it into states
fetch(`http://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${long}&APPID=${API_KEY}&units=metric`)
.then(res => res.json())
.then(json => {
// do all sorts of stuff with the weather json, putting it into states
});
// Fetch the aurora data
const myUTC = new Date().getTimezoneOffset();
fetch(`http://api.auroras.live/v1/?type=ace&data=bz&tz=${myUTC}&colour=hex`)
.then(res => res.json())
.then(json => {
// do stuff with the aurora json, put it into states
});
setIsLoaded(true); // this is for the activity indicator
}
}
getDataAsync();
return () => { ignore = true; }
}
}, []);
I'm getting the error when deliberately quickly switching out of the screen and back again while the activity indicator is spinning.
Return the cleanup outside of everything! let me know if it works
useEffect(() => {
let ignore = false;
if (Platform.OS === 'android' && !Constants.isDevice) {
errorMessage(
'Oops, this will not work on Sketch in an Android emulator. Try it on your device!',
);
} else {
// function to get location, weather and aurora info
const getDataAsync = async () => {
let {status} = await Permissions.askAsync(Permissions.LOCATION);
if (status !== 'granted') {
setErrorMessage('Permission to access location was denied');
}
if (!ignore) {
let location = await Location.getCurrentPositionAsync({});
// do stuff with the location data, putting it into states
fetch(
`http://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${long}&APPID=${API_KEY}&units=metric`,
)
.then((res) => res.json())
.then((json) => {
// do all sorts of stuff with the weather json, putting it into states
});
// Fetch the aurora data
const myUTC = new Date().getTimezoneOffset();
fetch(
`http://api.auroras.live/v1/?type=ace&data=bz&tz=${myUTC}&colour=hex`,
)
.then((res) => res.json())
.then((json) => {
// do stuff with the aurora json, put it into states
});
setIsLoaded(true); // this is for the activity indicator
}
};
getDataAsync();
}
return () => {
ignore = true;
};
}, []);
That was promising, but no, it didn't work. It may have to do with the fact that there are 2 async fetch requests and one "await" location request with each taking a different amount of time.
I am trying with abortController but that isn't working either:
useEffect(() => {
const abortController = new AbortController();
if (Platform.OS === 'android' && !Constants.isDevice) {
errorMessage(
'Oops, this will not work on Sketch in an Android emulator. Try it on your device!',
);
} else {
// function to get location, weather and aurora info
const getDataAsync = async () => {
let {status} = await Permissions.askAsync(Permissions.LOCATION);
if (status !== 'granted') {
setErrorMessage('Permission to access location was denied');
}
let location = await Location.getCurrentPositionAsync({signal: abortController.signal});
// do stuff with the location data, putting it into states
fetch(
`http://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${long}&APPID=${API_KEY}&units=metric`, { signal: abortController.signal })
.then((res) => res.json())
.then((json) => {
// do all sorts of stuff with the weather json, putting it into states
});
// Fetch the aurora data
const myUTC = new Date().getTimezoneOffset();
fetch(
`http://api.auroras.live/v1/?type=ace&data=bz&tz=${myUTC}&colour=hex`, { signal: abortController.signal })
.then((res) => res.json())
.then((json) => {
// do stuff with the aurora json, put it into states
});
setIsLoaded(true); // this is for the activity indicator
};
getDataAsync();
}
return () => {
abortController.abort();
}
}, []);
In addition to the memory leak error in the console, I am also getting:
Possible Unhandled Promise Rejection (id: 0):
[AbortError: Aborted]
Possible Unhandled Promise Rejection (id: 1):
[AbortError: Aborted]

How to add navigation function inside async function?

I have created an async function that fetches data from api and turns red, whenever the icon is presses, however now I want just after the like function is completed it navigates to another page, however am not able to do it.
Kindly help,
Below is the way that I had tried,
onButtonPress = async(item) => {
console.log(item)
console.log(this.state.buttonColor,'hello')
if(!this.state.likedItemIds.includes(item._id)){
try {
const response = await fetch("some url"+item._id);
const resJson = await response.text();
this.setState(prevState => ({
likedItemIds: [...prevState.likedItemIds, item._id]
}))
console.log(resJson)
if(this.state.buttonColor!=='white'){
this.props.navigation.navigate('Wishlist')
}
}
catch (error) {
console.error(error);
}
}
Do tell me if anything else is required and kindly help.
setState is an asynchronous function that allows you to pass a callback as the second argument. So you can do your navigation in that callback, which will fire after setState is complete. This means you will navigate away from the page once the like is complete.
You can do the below:
onButtonPress = async(item) => {
console.log(item)
console.log(this.state.buttonColor,'hello')
if(!this.state.likedItemIds.includes(item._id)){
try {
const response = await fetch("some url"+item._id);
const resJson = await response.text();
this.setState(prevState => ({
likedItemIds: [...prevState.likedItemIds, item._id]
}), () => {
// Do whatever else you need to do here (validation, etc.)
this.props.navigation.navigate('Wishlist')
})
}
catch (error) {
console.error(error);
}
}