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

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]

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

Fetching Data as a Json format in react native return error when mapping data

The error returned is : undefined is not an object (evaluating 'json.map') ,knowing that my api has multiple data.
The code i use to fetch data is :
CheckifReadLater = async () => {
const username = await AsyncStorage.getItem('username');
const isLoggedIn = await AsyncStorage.getItem('isLoggedIn');
if (isLoggedIn == 1 && username) {
await fetch(Config.backendAPI+`/readlater.php?username=${username}&select`)
.then((response) => {
reactotron.log("Response : ",response);
response.json();
})
.then((json) => {
json.map((product, index) => {
if (product.id == this.props.product.id) {
this.setState({
isReadLater: true,
})
}
});
})
.catch((error) => reactotron.log('This is the error: ',error))
}
reactotron.log("Readlater : ",this.state.isReadLater);
}
How can i solve this problem ?
You should return the response.json(); on the first .then function for it to be available on the next .then.
Do this:
await fetch(Config.backendAPI+`/readlater.php?username=${username}&select`)
.then((response) => {
reactotron.log("Response : ",response);
// update your code here
return response.json();
})

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

Problem in reusable code to check internet availability in react native

I have made a function that checks for internet availability. whenever I call this function it gives me true every time whether the internet is ON or OFF. I want to have one function that contains code to check the internet and I can call it before fetching data from the internet . my code is below.
const [campusList, setCampusList]= React.useState([{label:'Select Campus', value:'select campus'}]);
const isConnected =()=>{
NetInfo.fetch().then(state => {
console.log("Connection type", state.type);
console.log("Is connected?", state.isConnected);
if(state.isConnected)
return true;
else
return false;
});
}
const loadCampuses = async()=>{
if(isConnected)
{
await fetch(url)
.then((respons)=>respons.json())
.then((jsonResponse)=>{
jsonResponse.map((data)=>
setCampusList(campusList=> [...campusList, {label:data.Text, value:data.Value}])
);
})
.catch((error)=>console.log(error))
//.finally(()=>setLoading(false))
}
}
fetch Returns a Promise that resolves to a NetInfoState object. you need to wait promise to resolve
try this
const isConnected = sendRequest => {
NetInfo.fetch().then(state => {
if (state.isConnected) {
sendRequest();
}
});
};
const loadCampuses = () => {
isConnected(async () => {
await fetch(url)
.then(respons => respons.json())
.then(jsonResponse => {
jsonResponse.map(data =>
setCampusList(campusList => [
...campusList,
{ label: data.Text, value: data.Value },
]),
);
})
.catch(error => console.log(error));
});
};
oh right, it's a promise, not just a straight return. you need to await for it. You don't need a separate function:
if(await NetInfo.fetch().isConnected)

how to implement cache in react-native-video

How do we implement caching in react-native-video? Basically, when a video is currently streaming from a network resource, how do we save the video somewhere, and then retrieve it when the same resource is access. What is the best approach for this?
The best approach that i would refer you is using react-native-fetch-blob, you can implement it like this:
const RNFetchBlob = require('react-native-fetch-blob').default;
const {
fs
} = RNFetchBlob;
const baseCacheDir = fs.dirs.CacheDir + '/videocache';
//call the downloadVideo function
downloadVideo('http://....',baseCacheDir)
//Function to download a file..
const activeDownloads = {};
function downloadVideo(fromUrl, toFile) {
// use toFile as the key
activeDownloads[toFile] = new Promise((resolve, reject) => {
RNFetchBlob
.config({path: toFile})
.fetch('GET', fromUrl)
.then(res => {
if (Math.floor(res.respInfo.status / 100) !== 2) {
throw new Error('Failed to successfully download video');
}
resolve(toFile);
})
.catch(err => {
return deleteFile(toFile)
.then(() => reject(err));
})
.finally(() => {
// cleanup
delete activeDownloads[toFile];
});
});
return activeDownloads[toFile];
}
//To delete a file..
function deleteFile(filePath) {
return fs.stat(filePath)
.then(res => res && res.type === 'file')
.then(exists => exists && fs.unlink(filePath)) //if file exist
.catch((err) => {
// swallow error to always resolve
});
}
Cheers:)