How to customize the navigation structure to greatly improve the performance of your ReactNative application
Posted onEdited on
Where is the performance bottleneck of react-navigation
We can know that the best animation performance of ReactNative is LayoutAnimation UIView animation, The best gesture performance is to use NativeEvent. These two solutions point to one direction, that is, to solve the performance problem of ReactNative. We cannot let busy JS threads participate in these calls. However, react-navigation from onPress to navigation.push There are JS calls, so our task today is to customize a navigation architecture to greatly improve the performance of our ReactNative application
Encapsulate NavigationController
Starting from the animation, we need to use the UIView process animation that comes with Native. Then the iOS platform already has a UINavigationController library. We only need to create a UINavigationController on the Android platform.
We need to handle the hiding of the TabBar
We need to hide the Controller that has been covered by the top page to save memory
And we need to handle viewDidAppear and viewDidDisappear lifecycle
// NavigationController publicvoidpopToViewController(final HTRouteController controller, Boolean animated){ if (childControllerList. size() <= 1) { return; } if (this. lock) { return; } this. lock = true; finalint index = childControllerList. indexOf(controller); final HTRouteController animatedController = childControllerList. get(childControllerList. size() - 1); if (index == 0) { HTRouteTabBarController tabBarController = HTRouteGlobal.nextController(getView(), HTRouteTabBarController.class); if (tabBarController != null) { tabBarController. reloadShowTabBar(true); } } controller.getView().setVisibility(View.VISIBLE); translateAnimation(animatedController, animatedController.getView(), false, animated, new Callback() { @Override publicvoidinvoke(Object... args){ controller. viewDidAppear(); animatedController. viewDidDisappear(); int size = childControllerList. size(); List<HTRouteController> willRemoveControllerList = new ArrayList<>(); for (int i = index + 1; i < size; i ++) { HTRouteController removeController = childControllerList. get(i); willRemoveControllerList. add(removeController); } for (HTRouteController removeController: willRemoveControllerList) { removeChildController(removeController); } lock = false; } }); }
Package TabBar
From gesture analysis, we need to avoid JS.onPress, so we use the package of UITabBarController and UITabBar on the iOS platform. We only need to create a UITabBar and UITabBarController on the Android platform.
Due to the diversity of TabBar, we just encapsulate a common example, and other projects can inherit and rewrite
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
int bottom = 0; if (!TextUtils.isEmpty(HTRouteGlobal.getProp("ro.build.version.emui"))) { bottom = 1; } layoutParams.setMargins(0, 0, 0, bottom); fragmentContainer.addView(view, layoutParams);
fragment. viewDidAppear();
}
Encapsulate RouteView
The most important point is that we can bind the pages and parameters that need to be redirected to RouteView. When the original click event is received, we can directly jump to the native page without going through JS.
We need to leave the same interface as react-navigation on the JS side to facilitate switching at any time
JS parameter passing
If function parameters need to be passed between pages, we need to store these variables in global variables, and then retrieve them through the key on the second page, because functions cannot be serialized
const decodeRouteData = (props) => { let reloadProps = { ... props } let componentRouteOptionList = reloadProps?. componentRouteOptionList ?? {} let id = componentRouteOptionList?.id sureComponentItem(id)
let componentValueList = globalValue. componentList[id] ?? {} let componentPropList = componentValueList['componentPropList'] ?? {} reloadProps.componentPropList = componentPropList return reloadProps }
JS Lifecycle
We need to receive the viewDidAppear and viewDidDisappear life cycle events sent by the native side, and then call the method of the corresponding component through the id
let componentFunction = componentRef[actionName] if (componentFunction == null) { return } componentFunction. call(componentRef, value) })
Affiliated
The full code is here https://github.com/hellohublot/react-native-route I’m hublot, sharing some of my thoughts, I’m too busy with work, it’s inevitable that there will be negligence, please forgive me