React Native startup optimization - react-native

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.

Related

How to simulate an Appearance ("dark mode") change in React Native using Jest?

I am writing a puzzle app in React Native where the user can solve a puzzle by changing their device's Appearance to dark mode. I'm using React Native's Appearance module to accomplish this, by calling Appearance.getColorScheme() to get the initial color scheme, and registering an event listener for when the color scheme changes using Appearance.addChangeListener() within the useEffect hook:
useEffect(() => {
const originalColorScheme = Appearance.getColorScheme();
Appearance.addChangeListener((event) => {
if (Appearance.getColorScheme() !== originalColorScheme) {
setSolved(true);
}
});
}, []);
This is working fine, but I'd like to be able to unit test my component by simulating a change to dark mode in Jest and/or React Native Testing Library (or similar), after which I can check for changes to the content of the page (a "congratulations" message, in this case).
In my mind, it could look something like:
const text = await screen.findByText("good luck!");
// fireEvent("changeColorScheme", { colorScheme: "dark" });
const text2 = await screen.findByText("congratulations!");
Is anything like this possible? I'm guessing it has something to do with mocks, but it's a little over my head at the moment. Thanks for any help you can provide!

How to run code on App Start-up in React-Native

So I have a react-native application and want to run some code on app start-up;
The application has background task handlers(android) which (to the best of my knowledge) does not mount any views so initializing stuff in the root constructor or componentDidMount may not work.
I want to add certain database listeners to my application which get triggered even while the app is being run in background.
Any help on the same would be highly appreciated.
Thanks regards.
Amol.
In functional components, you want to use the useEffect() method with an empty dependency array:
useEffect(() => {
// this code will run once
}, [])
When an empty dependency array ([]) is used, the useEffect() callback is called only once, right after the component renders for the first time.
Use like this:
import React, { useEffect } from 'react'
export default function App () {
useEffect(() => {
// this code will run once
}, [])
// ...
}
React-Native has a function super() which is same as constructor() that will work when your application get started. For example if you write a alert message on your super() function('When a user open your app, an alert message will be display'. You are able to get data using super() function when your app is opened)
super(){
alert('app started')
}

Expo - Continue Upload on if component unmount

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.

For a react native app, is there a way to view console.log output in production?

For a react native app, is there a way to see console.log output in production? As far as I know this is impossible.
I know that I can write important messages to a file or to a remote database. I am looking for a simpler solution... Have you implemented such functionality using a github package that you recommend?
I am looking for a solution for android.
Get fancy by writing and using your own Component <ConsoleLog>
const ConsoleLog = ({ children }) => {
console.log(children)
};
Then use it:
render() {
return (
<div>
<h1>List of todos</h1>
<ConsoleLog>{ this.props.todos }</ConsoleLog>
</div>
);
}
if you want to log to file, use react-native-file-log package

Preloading assets in React-Native expo app never finishes

I'm writing a simple app with React Native and Expo.
This app has ~10 small to medium sized images that are used in different places within the app.
From what I read, unless I cache these images, they will be required to be downloaded from expo each time.
For this reason, I have noticed that they seem to load in really slowly when testing the app. Upon building and navigating through the app, I find that it takes a few seconds for my images to pop up even after the rest of the page has loaded.
I followed the setup as seen in the starting template.
Here is what my App.js looks like (I am using react-navigation so it varies from the sample file above):
export default class App extends React.Component {
state = {
isLoadingComplete: false
};
componentDidMount() {
StatusBar.setHidden(true);
}
render() {
_loadResourcesAsync = async () => {
return Promise.all([
Asset.loadAsync([
require("./assets/syria.png"),
require("./assets/lebanon.png"),
require("./assets/kenya.png"),
require("./assets/indonesia.png"),
require("./assets/somalia.png"),
require("./assets/india.png"),
require("./assets/america.png"),
require("./assets/albania.png"),
require("./assets/bosnia.png")
])
]);
};
_handleLoadingError = error => {
Alert.alert(error);
};
_handleFinishLoading = () => {
this.setState({ isLoadingComplete: true });
};
if (this.state.isLoadingComplete == false) {
return (
<AppLoading
startAsync={this._loadResourcesAsync}
onError={this._handleLoadingError}
onFinish={this._handleFinishLoading}
/>
);
} else {
return (
<AppContainer
ref={navigatorRef => {
NavigationService.setTopLevelNavigator(navigatorRef);
}}
/>
);
}
}
}
I have excluded my react-navigation code for the sake of brevity.
When I run this, my app gets stuck at Downloading JavaScript bundle 100.00%.
It seems that the _handleFinishLoading never runs. At least, that's the only reason I can see for it to never finish loading.
Given the small amount of images, I don't know how this could take more than a second. Instead it sits at the splash screen forever.
Any ideas on what I might be doing wrong here?
Found the solution:
I made a simple error. The async functions (_loadResourcesAsync, _handleFinishLoading, etc) need to be outside the render method. Moving them below my render method inside of the app class caused this to work as expected.