Expo - Continue Upload on if component unmount - react-native

In my react native (Expo) application the user has the possibility to select images for a group from the gallery to upload them to the server.
When the User selects the images in the Mediapicker Screen i store the list of the uris in the Asyncstorage and navigate back to the "group screen".
Here i read the data from the asyncstorage
_retrieveUpload = async () => {
try {
const value = await AsyncStorage.getItem("upload_que");
if (value !== null) {
this.setState({ uploadQueue: JSON.parse(value) });
}
} catch (error) {
console.log(error);
}
};
in the next step i created a component which takes this.state.uploadQueue as property, iterate over the values and upload them to the server.
This is working as long as the component is mounted and the user does not leave the screen.
I read that there is no possibility for a background-task in expo, but how can i start something like a service which runs independent from the actual mounted component?

First of all, can you post you Upload Component source code for more details.
Also please try in your upload code if you have await syntax, remove it and work with 'promise' and 'then' syntax if you need to do something after upload.

Related

My Firebase Realtime Database not working in React Native

I'm having trouble with my Realtime Database integration inside my React Native project. I'v followed the install guide from here https://rnfirebase.io/database/usage(did the initial getting started install also). I'v tried reading a value from the database but NOTHING gets logged in the console.log.
import database from '#react-native-firebase/database';
function TestComponent( props ) {
useEffect(() => {
fetchData();
}, []);
const fetchData = async () => {
database().ref('/did').once('value').then(snapshot => {
console.log('DATA: ', snapshot.val());
});
};
return (
<View>
<Button title="DATA" onPress={fetchData} />
</View>
);
}
My database structure looks like this:
I'v tried everything. Made the function async and not async, moved the function inside useEffect(), took out useEffect() and left a normal function that run with a button click. I'v tried to fetch the data using Axios and adding .json to the URL but NOTHING logs data, I don't know if the database doesn't answer or if the code is the problem. My database().ref() is working fine because when I log it points to the reference URL. Also my rules are true for both read and write so no problem there. Used Postman to make a fetch and it worked perfectly.

Abort an Updates.fetchUpdateAsync() after a certain time [Expo/React native]

Expo React Native SDK Version: 46
Platforms: Android/iOS
Package concerned : Expo.Updates
Hello everyone, I want to programmatically check for new updates, without using the fallbackToCacheTimeout in app.json that will trigger the check of the new updates when the application is launched because like that I can't put a custom loading page.
So by doing this all by code as follow :
try{
const update = await Updates.checkForUpdateAsync();
if(update.isAvailable){
await Updates.fetchUpdateAsync();
await Updates.reloadAsync();
}else{}
}catch(err){}
But I want to be able to abort all those calls after a certain time (thus, the user that have a bad connection can use the app without waiting a very long time).
I check the documentation and I cannot found any method that allow this.
I dont't think it's possible to cancel a Promise for now in Javascript, or maybe any connection ?
Or does the "fallbackToCacheTimeout" value in the app.json will automatically apply to the fetch updates call of the Expo API?
Do someone have any idea how to do it ? :(
First of all I am assuming you have set updates.checkautomatically field to ON_ERROR_RECOVERY in app.json or app.config.js file. If not, please check the documentation. The reason why you need this is to avoid automatic updates which can also block your app on splash screen.
Updated Solution
Because of the limitation in javascript we can't cancel any external Promise (not created by us or when its reject method is not exposed to us). Also the function fetchUpdateAsync exposed to us is not a promise but rather contains fetch promise and returns its result.
So, here we have two options:
Cancel reloading the app to update after a timeout.
But note that updates will be fetched in background and stored on
the device. Next time whenever user restarts the app, update will
be installed. I think this is just fine as this approach doesn't
block anything for user and also there is a default timeout for http
request clients like fetch and axios so, request will error out in
case of poor/no internet connection.
Here is the code:
try {
const update = await Updates.checkForUpdateAsync();
if (update.isAvailable) {
const updateFetchPromise = Updates.fetchUpdateAsync();
const timeoutInMillis = 10000; // 10 seconds
const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject("timedout"), timeoutInMillis))
// This will return only one Promise
Promise.race([updateFetchPromise, timeoutPromise])
.then(() => Updates.reloadAsync())
.catch((error) => {
if (error === 'timedout') {
// Here you can show some toast as well
console.log("Updates were not cancelled but reload is stopped.")
} else if (error === 'someKnownError') {
// Handle error
} else {
// Log error and/or show a toast message
}
})
} else {
// Perform some action when update is not available
}
} catch (err) {
// Handle error
}
Change the expo-updates package just for your app using a patch
Here you can return a cancel method with Updates.fetchUpdateAsync() and use it with setTimeout to cancel the fetch request. I won't be providing any code for this part but if you are curious I can definitely provide some help.
Please refer this section to understand use of fallbackToCacheTimeout in eas updates.
Old solution:
Now, for aborting or bypassing the promise i.e. Updates.fetchUpdateAsync in your case. You can basically throw an Error in setTimeout after whatever time duration you want, so that, catch block will be executed, bypassing the promises.
Here is the old code :
try{
const update = await Updates.checkForUpdateAsync();
if(update.isAvailable){
// Throw error after 10 seconds.
const timeout = setTimeout(() => { throw Error("Unable to fetch updates. Skipping..") }, 10000)
await Updates.fetchUpdateAsync();
// Just cancel the above timeout so, no error is thrown.
clearTimeout(timeout)
await Updates.reloadAsync();
}else{}
}catch(err){}

Testing Branch.IO referral links on react native

We use branch for referrals in our react native application. I have implemented branch successfully, now I want to test some scenarios.
When we click on a referral link, it navigates to app store or play store. But I want to do some debugging to identify if the params I pass are successfully send or not.
I have subscribed it like this. But how can I debug this with react-native debugger to see the console logs?
BranchIO.subscribe(async ({ error, params }) => {
if (error) {
console.log('Error from Branch: ', error);
return;
}
// Handle non-Branch URL.
if (params['+non_branch_link']) return;
// Indicates initialization success.
// No link was opened.
if (!params['+clicked_branch_link']) return;
const installParams = await BranchIO.getFirstReferringParams();
if (installParams?.$canonical_identifier === DeepLinkTypes.referral) {
store.dispatch(setReferralKey(installParams.referralKey));
}
// A Branch link was opened.
// Route link based on data in params
navigatePath(params.$deeplink_path);
});
To debug if you are able to fetch the associated link data or not you can put in a debugger at the following line-
const installParams = await BranchIO.getFirstReferringParams();
Also we would suggest you to kindly use/update the above line to use getLatestReferringParams
let lastParams = await branch.getLatestReferringParams()
instead of getFirstReferringParams() since there are some recent issues reported lately with this method.
You can print out the parameters in the console and see for the link data.

React Navigation - Check on which screen the user is

I am using react-navigation in my project and I need to detect if the user is on the Dashboard/Graph/Posts page.
For example, if I am on Posts page, I need a param to write a conditional.
e.g. Make a request if I am only at Posts page
Is it possible to check on which screen the user is?
You can get it through navigation object, try it
this.props.navigation.state.routeName
You can try the following,
on componentDidMount
componentDidMount() {
this.subs = this.props.navigation.addListener("didFocus", payload => {
console.log("PAY_LOAD...", payload);
// Check the payload and your logic(you can get the current screen name like => payload.state.routeName)
});
}
on componentWillUnmount
componentWillUnmount() {
this.subs.remove();
}
Note: Don't forgot to remove listener on 'componentWillUnmount()'

React Native startup optimization

I am looking for a way to optimize the startup time of a pure react native mobile app.
As a JavaScript framework, is that possible to bundle the JavaScript files into separated files, say something like common.js and app.js. I was searching via Google with keywords something like "react native webpack" stuff but it seems like all these libraries are deprecated or out of date, such as react-native-webpack-server, react-native-webpack-starter-kit etc.
I am wondering if anybody here is also looking for a way to optimize the JavaScript bundle in react native. Or, maybe these third party bundle approach has been overcame by Facebook standard bundle?
You could dynamically load your component, in this way your bundle.js will contain only the fraction of js needed and as you navigate you will request the other different parts / fractions.
Rather than do the traditional way: import App from './containers/App/App'; you could do something like this:
class ImportedComponent extends Component {
state = {
component: null
}
componentWillMount() {
this.props.load()
.then((mod) => this.setState(() => ({
component: mod.default
})))
}
render() {
return this.props.children(this.state.component)
}
}
const App = (props) => (
<ImportedComponent load={() => import('./containers/App/App')}>
{(Component) => Component === null ? <h6 className="loading-message">Loading...</h6> : <Component {...props}/>}
</ImportedComponent>
)
or you can lazy load your component itself. Let's say for example that I have Moment JS and I don't want to load it until it's needed. so what I could do:
1) Create a state and set it to null.
constructor(props){
super(props);
this.state = {
lazyLoadedComponent: () => null
}
}
2) Use async componentDidMount with await, try and catch and update the state lazyLoadedComponent on componentDidMount
async componentDidMount(){
try {
const Moment = await import('react-moment');
this.setState({ lazyLoadedComponent: (data)=>{
return React.createElement(Moment.default, {format:'MM/DD/YY'}, data)
}
});
}
catch(err) {
this.setState({ lazyLoadedComponent: <div>{`Failed to load component: ${err}`}</div> });
}
}
3) Call the component on the render:
{this.state.lazyLoadedComponent(value.createdOn)}
By following these 2 examples you should, hopefully, be looking at a bundle.js under 250KB.
As a possible solution you can use ram-bundle format, that metro bundler provides.
In this case you will not load the entire js-bundle - you will load only part, that you need at a startup (in a lot of application are a lot of places, which user may not even see, and this feature allow you load such parts, only when they are required). So you can simplify your entry point and load only small piece of your bundle.
You can look at react-native-bundle-splitter. This library well integrated with almost all popular navigation libraries and allows you to postpone a loading of specific routes. For example, if you have a login screen, you can load at start up only this screen, and all others load in background or start the loading of them, only when user can see them.