Lazy Load screens with React Native Router Flux - react-native

There are several screens in my React Native application.
I am using React Native 0.56 and React Native Router Flux.
Is there a way for a screen to load all of its imports when the screen is actually needed, and not when the app is launched and bundle is parsed?
Because of this the initial load time of the app is increasing.
I came across RAM format in the React Native Performance Section.
https://facebook.github.io/react-native/docs/next/performance.html#enable-the-ram-format
How can I reduce the number of modules in loadedModuleNames??
index.android.js
import { AppRegistry } from 'react-native';
import AppContainer from './src/';
const modules = require.getModules();
const moduleIds = Object.keys(modules);
const loadedModuleNames = moduleIds
.filter(moduleId => modules[moduleId].isInitialized)
.map(moduleId => modules[moduleId].verboseName);
const waitingModuleNames = moduleIds
.filter(moduleId => !modules[moduleId].isInitialized)
.map(moduleId => modules[moduleId].verboseName);
// make sure that the modules you expect to be waiting are actually waiting
console.log(
'loaded:',
loadedModuleNames.length,
'waiting:',
waitingModuleNames.length
);
// grab this text blob, and put it in a file named packager/modulePaths.js
console.log(`module.exports = ${JSON.stringify(loadedModuleNames.sort())};`);
AppRegistry.registerComponent('Roots', () => AppContainer);
navigation/index.js
/**
* App Navigation
*
*/
import React from 'react';
import { Actions, Scene, ActionConst, Stack } from 'react-native-router-flux';
// Consts and Libs
import { AppConfig } from '#constants/';
// Components
// Scenes
import Placeholder from '#components/general/placeholder/index';
import ExperiencesScenes from '#navigation/experiences';
import AuthScenes from '#navigation/auth';
import MediateScene from '#navigation/mediate';
import HomeScene from '#navigation/home';
import SearchScene from '#navigation/search';
import NoticesScene from '#navigation/notices';
// import ShortlistedMapScene from '#navigation/shortlisted_map';
import IntroScene from '#navigation/intro';
import AssistantScene from '#navigation/assistant';
import NotificationSettingsScene from '#navigation/notification_settings';
import HighlightScene from '#navigation/highlight';
import TransportScene from '#navigation/transport';
import HelpScene from '#navigation/help';
import FaqScene from '#navigation/faq';
import DynamicListScene from '#navigation/component_list';
import BrandCoverScene from '#navigation/brand_cover';
import ListingsScene from '#navigation/listings';
import ShorlistedScene from '#navigation/shortlisted_list';
import SettingsScene from '#navigation/settings';
import OnTheWayScene from '#navigation/on_the_way';
import ItinerariesScene from '#navigation/itineraries';
import TempScene from '#navigation/temp';
import TipsScene from '#navigation/tips';
import SimpleSwipe from '#navigation/simple_swipe';
import Cities from '#navigation/cities';
// import ReferralScene from '#navigation/referral';
/* Routes ==================================================================== */
export default Actions.create(
<Stack key={'root'}>
{MediateScene}
{IntroScene}
{HomeScene}
{AssistantScene}
{SearchScene}
{NoticesScene}
{ShorlistedScene}
{SettingsScene}
{ListingsScene}
{TipsScene}
{BrandCoverScene}
{AuthScenes}
{ExperiencesScenes}
{NotificationSettingsScene}
{HighlightScene}
{TransportScene}
{HelpScene}
{FaqScene}
{DynamicListScene}
{OnTheWayScene}
{ItinerariesScene}
{SimpleSwipe}
{Cities}
</Stack>
);
Navigation Code looks similar to this
import React from 'react';
import { Scene, ActionConst } from 'react-native-router-flux';
// Consts and Libs
import { AppConfig } from '#constants/';
import { AppStyles, AppSizes, AppColors } from '#theme/';
// Scenes
import BrandCoverScreen from '#screens/brand_cover/index';
const navbarPropsTabs = {
...AppConfig.navbarProps,
hideNavBar: true,
};
/* Routes ==================================================================== */
const scenes = (
<Scene
{...navbarPropsTabs}
key={"brand_cover"}
component={BrandCoverScreen} >
</Scene>
);
export default scenes;

That's a bit more complicated then it seems - and it's not related to react-native-router-flux only - it would require support from metro to split the JS bundler into smaller pieces and load it in the right way.
It seems that Expo might do something like that to assets, but for the JS code I only saw something close to it hacking the react-native code in this article:
Lazy Bundle Loading in React Native by Karan Thakkar
https://medium.com/react-native-training/lazy-bundle-loading-in-react-native-5f717b65482a
And here the GitHub repository with the code used in the article:
https://github.com/karanjthakkar/RNLazyBundleLoading
In case the article or the repository goes offline, here is the diff he also showed in the article (this is a diff between original react-native and his fork:
diff --git a/React/Base/RCTBridge+Private.h b/React/Base/RCTBridge+Private.h
index 1995801ffcaa..cf1609bc9614 100644
--- a/React/Base/RCTBridge+Private.h
+++ b/React/Base/RCTBridge+Private.h
## -90,6 +90,12 ## RCT_EXTERN void RCTVerifyAllModulesExported(NSArray *extraModules);
*/
- (void)start;
+/**
+ * Called on the child bridge to run the executor and start loading
+ * the partial bundle
+ */
+- (void)lazyStart;
+
/**
* Used by RCTModuleData to register the module for frame updates after it is
* lazily initialized.
diff --git a/React/Base/RCTBridge.h b/React/Base/RCTBridge.h
index 5222a15b5ca7..d0be4652f4f5 100644
--- a/React/Base/RCTBridge.h
+++ b/React/Base/RCTBridge.h
## -223,4 +223,9 ## RCT_EXTERN NSString *RCTBridgeModuleNameForClass(Class bridgeModuleClass);
*/
- (BOOL)isBatchActive;
+/**
+ * Load a custom bundle into an existing bridge instance
+ */
+- (void)loadCustomBundle:(NSString *)bundleName;
+
#end
diff --git a/React/Base/RCTBridge.m b/React/Base/RCTBridge.m
index cee9aa0463e1..c1b3ae107599 100644
--- a/React/Base/RCTBridge.m
+++ b/React/Base/RCTBridge.m
## -319,6 +319,13 ## - (void)setUp
RCT_PROFILE_END_EVENT(RCTProfileTagAlways, #"");
}
+-(void)loadCustomBundle:(NSString *)bundleName
+{
+ _bundleURL = [NSBundle.mainBundle URLForResource:bundleName withExtension:#"jsbundle"];
+
+ [self.batchedBridge lazyStart];
+}
+
- (BOOL)isLoading
{
return self.batchedBridge.loading;
diff --git a/React/CxxBridge/RCTCxxBridge.mm b/React/CxxBridge/RCTCxxBridge.mm
index 4b7aaf375e5b..32df68ac43ec 100644
--- a/React/CxxBridge/RCTCxxBridge.mm
+++ b/React/CxxBridge/RCTCxxBridge.mm
## -382,6 +382,29 ## - (void)start
RCT_PROFILE_END_EVENT(RCTProfileTagAlways, #"");
}
+- (void)lazyStart
+{
+ __weak RCTCxxBridge *weakSelf = self;
+
+ __block NSData *sourceCode;
+ [self loadSource:^(NSError *error, RCTSource *source) {
+ if (error) {
+ [weakSelf handleError:error];
+ }
+
+ sourceCode = source.data;
+ RCTCxxBridge *strongSelf = weakSelf;
+ if (sourceCode) {
+ [strongSelf executeSourceCode:sourceCode sync:NO];
+ }
+ } onProgress:^(RCTLoadingProgress *progressData) {
+#if RCT_DEV && __has_include("RCTDevLoadingView.h")
+ RCTDevLoadingView *loadingView = [weakSelf moduleForClass:[RCTDevLoadingView class]];
+ [loadingView updateProgress:progressData];
+#endif
+ }];
+}
+
- (void)loadSource:(RCTSourceLoadBlock)_onSourceLoad onProgress:(RCTSourceLoadProgressBlock)onProgress
{
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];

Related

React native None of these files exist: error?

the path of the file is correct but I get the following error
`None of these files exist:
* src\pages\Test(.native|.android.js|.native.js|.js|.android.jsx|.native.jsx|.jsx|.android.json|.native.json|.json|.android.ts|.native.ts|.ts|.android.tsx|.native.tsx|.tsx)
* src\pages\Test\index(.native|.android.js|.native.js|.js|.android.jsx|.native.jsx|.jsx|.android.json|.native.json|.json|.android.ts|.native.ts|.ts|.android.tsx|.native.tsx|.tsx)
7 |
8 | import React from 'react';
> 9 | import Test from './src/pages/Test';
| ^
10 |
11 | function App() {
12 | return (]`
FOLDER STRUCTURE;
show
created new project.
import react from 'react';
import {View, Text} from 'react-native';
export default function Test() {
return (
<View>
<Text>Test</Text>
</View>
);
}

Three js No image Material : WARNING in asset size limit

I'm working on a threeJs project and I got this 3 errors on npm run build :
1
WARNING in asset size limit: The following asset(s) exceed the recommended size limit (244 KiB).
This can impact web performance.
Assets:
assets/images/67a542cb789140671d2491d8df477a72.png (4.8 MiB)
assets/images/6376a942e1ef27fea4331307af793e1d.png (658 KiB)
bundle.4cab645ddfe7a1d96c4e.js (3.19 MiB)
2
WARNING in entrypoint size limit: The following entrypoint(s) combined asset size exceeds the recommended limit (244 KiB). This can impact web performance.
Entrypoints:
main (3.19 MiB)
main.css
bundle.4cab645ddfe7a1d96c4e.js
3
WARNING in webpack performance recommendations:
You can limit the size of your bundles by using import() or require.ensure to lazy load some parts of your application.
For more info visit https://webpack.js.org/guides/code-splitting/
Here is the code :
import './style.css'
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
import * as dat from 'dat.gui'
import map from './texture/earth.jpg'
import mapdump from './texture/earthbump.jpg'
import clouds from './texture/earthCloud.png'
import galaxy from './texture/galaxy.png'
// global variables
let scene;
let camera;
let renderer;
const canvas = document.querySelector(".webgl");
...
// renderer setup
renderer = new THREE.WebGLRenderer({
canvas: canvas,
antialias: true,
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio ? window.devicePixelRatio : 1);
renderer.autoClear = false;
renderer.setClearColor(0x000000, 0.0);
...
// earth
const earthGeometry = new THREE.SphereGeometry(1, 32, 32);
const earthMaterial = new THREE.MeshPhongMaterial({
map: new THREE.TextureLoader().load(map),
bumpMap: new THREE.TextureLoader().load(mapdump),
roughness: 1,
metalness: 0,
bumpScale: 0.3,
});
const earthMesh = new THREE.Mesh(earthGeometry, earthMaterial);
scene.add(earthMesh);
// cloud
const cloudGeometry = new THREE.SphereGeometry(1.02, 30, 30);
const cloudMetarial = new THREE.MeshPhongMaterial({
map: new THREE.TextureLoader().load(clouds),
transparent: true,
});
const cloudMesh = new THREE.Mesh(cloudGeometry, cloudMetarial);
scene.add(cloudMesh);
...
npm run dev
how it shoud be
dist
how it is on dist
Link on github
Github

How to export and import js script?

Hello I have a code like this which works fine in main.js
Vue.prototype.$assetsResolution =
document.body.clientWidth * devicePixelRatio <= 1920 && document.body.clientHeight * devicePixelRatio <= 1080
? 1080
: 2160;
However, I have a lot of similar things and I would like to move the prototype code into another file.
For example, I tried to do it like this
//main.js
import "./vue-extensions/prototypes"
//prototypes.js
import Vue from "vue"
export {
Vue.prototype.$assetsResolution =
document.body.clientWidth * devicePixelRatio <= 1920 &&
document.body.clientHeight * devicePixelRatio <= 1080
? 1080
: 2160
}
But I have an error Unexpected token, expected "," in Vue.prototype
That syntax looks incorrect.
The external file does not need to export anything. It should just include the Vue.prototype.$assetsResolution assignment:
// prototypes.js
import Vue from 'vue'
Vue.prototype.$assetsResolution =
document.body.clientWidth * devicePixelRatio <= 1920 &&
document.body.clientHeight * devicePixelRatio <= 1080
? 1080
: 2160;
demo 1
You could also make this a Vue plugin by exporting a function that receives the Vue to modify:
// prototypes.js
export default Vue => {
Vue.prototype.$assetsResolution =
document.body.clientWidth * devicePixelRatio <= 1920 &&
document.body.clientHeight * devicePixelRatio <= 1080
? 1080
: 2160;
}
...which would be used like this:
// main.js
import Vue from 'vue'
import MyPlugin from './prototypes'
Vue.use(MyPlugin)
demo 2

object circe is not a member of package io

I am trying to create a predef.sc file for ammonite REPL. This is what I have written
val fs2Version = "2.2.2"
val circeVersion = "0.13.0"
// fs2
interp.load.ivy("co.fs2" %% "fs2-core" % fs2Version)
import scala.collection.immutable.{Stream => _}
import scala.{Stream => _}
import _root_.fs2._
// circe
interp.load.ivy("io.circe" %% "circe-core" % circeVersion)
interp.load.ivy("io.circe" %% "circe-parser" % circeVersion)
interp.load.ivy("io.circe" %% "circe-generic" % circeVersion)
import _root_.io.circe._, _root_.io.circe.parser._, _root_.io.circe.syntax._, _root_.io.circe.optics.JsonPath._, _root_.io.circe.generic.auto._
But it gives me an error saying
object circe is not a member of package io
I think its because fs2 also has a sub package called "io"
If you are using Intellij checkout this question How to force IntelliJ IDEA to reload dependencies from build.sbt after they changed? it did work for me and I was having your same error.
Or if you are using VSCode see this https://scalameta.org/metals/docs/editors/vscode/ you basically have to Ctrl + Shift + P and type import build but you gonna need the Scala (Metals) extension to do that.
Works for me with the following predef.sc file:
import $ivy.`org.typelevel::cats-core:2.1.1`, cats._, cats.implicits._
import $ivy.`org.typelevel::cats-effect:2.1.1`
import $ivy.`co.fs2::fs2-core:2.2.2`
import $ivy.`io.circe::circe-core:0.13.0`
import $ivy.`io.circe::circe-parser:0.13.0`
import $ivy.`io.circe::circe-generic:0.13.0`
import $ivy.`io.circe::circe-optics:0.13.0`
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
import scala.concurrent.Future
import scala.util.{Failure, Success}
import scala.concurrent.Await
import scala.collection.immutable.{Stream => _}
import scala.{Stream => _}
import _root_.fs2._
import _root_.io.circe._, _root_.io.circe.parser._, _root_.io.circe.syntax._, _root_.io.circe.optics.JsonPath._, _root_.io.circe.generic.auto._
and all your imports after this

Export URL for code organisation

I'm fairly new to React native, and i would like to have all my endpoints URLs in the same place
So, i have in my environement.js:
module.exports = {
SERVER: 'http://url.com',
API_VERSION: '/api/v3',
FULL_URL: this.SERVER + this.API_VERSION,
PLAYERS: this.FULL_URL + '/players',
TEAMS: this.FULL_URL + '/teams',
};
And on the other side in getGame.js
GLOBAL = require('../environement');
function getGame() {
console.log(GLOBAL.PLAYERS)
}
When i run my app i got
undefined/players
What am i doing wrong?
Thanks
You can try something like this using ES6.
const SERVER = 'http://url.com';
const API_VERSION = '/api/v3';
export const FULL_URL = SERVER + API_VERSION;
export const PLAYERS = FULL_URL + '/players';
export const TEAMS = FULL_URL + '/teams';
And then
import { FULL_URL, PLAYERS, TEAMS } from 'path/to/the/file'
Please take a look to this solution: https://github.com/luggit/react-native-config .
Also it will be very helpful when you will be have a few different environments and build agent for app in teamcity for example.