24 tips for React Native you probably want to know
React native is amazing. But things could be tough for times. Here are something I’ve from project to project that I want to share with you, which may save you some time in the future.
1. For debug
1.1 Breakpoint
When IDE goes mad, like the Visual studio code or Webstorm won’t stop at the breakpoint, besides figuring it out by walking through their Github issue. You can simply put a debugger
in your code, then it will stop the execution during runtime.
1.2 Attach the packager process
When you console.log
during dev-time, your information will be shown in the browser console. You can see that console information right from your IDE. If you use react-native-tools
for Visual Studio code. You just go to the debug tab and run that Attach to packager
command and reload the app. But, you need to:
- Close Chrome, because only one debugger should be attached for one packager process
- Packager itself needs to be opened.
The benefits are not only hot module reloading stills works, but also you can debug your code when you writing them, right inside IDE.
1.3 Atom
Yes, VSC is just much better but you need to know that you can literally open react debug tools (like the one you installed in Chrome) right inside Atom… And, yes, Nuclide supports RN, again… Just want to let you know…
1.4 How to inspect the bridge
We all know there is a bridge between the native and js side. React native uses it to communicate between the two to do the UI updates and more. This is how you can inspect it. Just add the following code to your index.js
:
1 | import MessageQueue from "react-native/Libraries/BatchedBridge/MessageQueue.js"; |
Then in the console of your debugger, you should see lots of messages:
1 | Object {type: 0, module: "JSTimers", method: "callTimers", args: Array(1)} |
The type field indicates the direction:
- If
type
equals0
that means that the data is going from native to JavaScript; - if it equals
1
, the data goes from JavaScript to native.
1.5 Native debug, a must have
Xcode
: After you running the app, Press the Debug View Hierarchy
button, it will show all your views in a 3D way. You can inspect your full view tree in a very visual appealing way. This is a fantastic way to make yourself feel guilty because now you know how complex your UI actually is even though it seems to adopt a minimalist design concept. Just look at the google image to see how cool it is.
Android studio
: Be sure to check Android Profiler
, an awesome tool to analyze your app’s performance on Android. CPU, memory, network, you get them all.
1.6 Check the native log
Just in case you forgot:
react-native log-ios
react-native log-android
You can see the native log from the command line.
1.7 Recommend
One lovely debugging tool is reactotron. A desktop app for inspecting your React JS and React Native projects. macOS, Linux, and Windows. Really beautiful.
If you are using Mac. react-native-debugger is another good candidate. It combines React Inspector / Redux DevTools together. Just two steps.
- Install:
brew update && brew cask install react-native-debugger
- Run:
open "rndebugger://set-debugger-loc?host=localhost&port=8081"
1.8 Continues getting Packager is not running
after refreshing
sometimes everything is right, but you still get that packager is not running
message, instead of using CMD+R to refresh the page, you just need to click the bottom Refresh Page
button, I know, they should be the same. But…
1.9 For the YellowBox
Yes, you can use YellowBox.ignoreWarnings(['Warning: isMounted(...) is deprecated',])
to ignore the warnings which showed in a yellow box. But it might be better that you put the link to the GitHub issue. Just in case you can check it regularly, and remove it finally.
2. You are in a different environment
2.1 Different result
In most cases, React Native will use JavaScriptCore, the JavaScript engine that powers Safari. Note that on iOS, JavaScriptCore does not use JIT due to the absence of writable executable memory in iOS apps.
When using Chrome debugging, all JavaScript code runs within Chrome itself, communicating with native code via WebSockets. Chrome uses V8 as its JavaScript engine.
This is something very important to remember, a case that I’ve been dealt with is moment.js
actually behaves differently. For instance, this month is May, so for the following code:
1 | moment().month(); |
It works ONLY when you test via jest
(because you are using Node, and the behaviour is expected as the documentation), but when you run it in the react native environment. You will get 5.
So, the takeaways here are:
Date
is the dark side of react native likenavigation
, remember this difference.- Don’t always question yourself.
To your surprise, when you run your storybook react native in the simulator, the runtime is still not JSC, so the above result will be 4 again…
2.2 Update your JSC for android
I forgot, but in one extreme case, update the JavaScriptCore actually solves the problem. The official jsc-android is not been updated since 2016. Use the community one instead, jsc-android-buildscripts
Open the link and skip to the How to use it with my React Native app
part. It is actually quite easy.
3. Update your Android project
All the settings are too old if you are a native developer, rather than compatibility old. It’s more like centuries old and lost love from the team. But everything is transparent. Still, before update, be sure there are not any legacy plugins which still needs the old version. But still you can just update, and git reset
if anything wrong happens.
3.1 Update gradle
Gradle is the building tool used in the JVM world.
Open android/gradle/wrapper/gradle-wrapper.properties
, find the distributionUrl=
line
Change the gradle version to 4.5.1
so the result looks like this:
distributionUrl=https\://services.gradle.org/distributions/gradle-4.5.1-all.zip
3.2 Update your android project right after generating the template
- Open build.gradle under android/app/
- find the android { } block
- Change the following version to the below:
- compileSdkVersion 27
- buildToolsVersion “27.0.3”
- minSdkVersion 16
- targetSdkVersion 27
- In the dependencies block, change the
appcompat
line to match the target version
compile "com.android.support:appcompat-v7:27.1.1"
- In the
android/build.gradle
Update the android build toolsclasspath 'com.android.tools.build:gradle:3.1.2'
3.3 Reduce your APK
By default, APK generated by React native includes JSCore binaries for both x86 and ARM. If you don’t need x86 you could reduce the size.
Add the following line to your /android/app/build.gradle
:
1 | def enableSeparateBuildPerCPUArchitecture = true |
It should generate 2 APK, one for x86 and one for ARM, upload all of them to the Google Play, the user will get the proper one according to their device.
3.4 Gradle
If you want learn something about gradle, I suggest you start from my blog: Starting with gradle, creating a simple build.gradle. It is much easy to follow than the official document and just cost maybe 10 minutes.
4. Image, Image, Image
4.1 Use WebP could potentially saves 20% space. And end up in a smaller packager
But for Android, you need to add something to gradle. From the official document:
You will need to add some optional modules in
android/app/build.gradle
, depending on the needs of your app.
1 | dependencies { |
4.2 For current version of RN
You can just require('/img/mine.png')
to load it.
4.3 This is how you do it natively
Sometimes, even put the image there will make your app feels much faster.
For iOS
- Open Xcode, find
Images.xcassets
, drag the static assets into it. - Make sure it is included in the building phase: Build Phases -> Copy Bundle Resources.
- Use it like this:
1 | <Image source={{uri: 'goodImage'}} /> |
No need for the extension, and you might need to add width
and height
for actually rendering it.
For Android
- Open Android studio
- Drag your images to this folder:
android/app/src/main/res/drawable
- The filename should start with the letter
- Then use it like the iOS
5. React technique still apply
5.1 PureComponent is not enough
If you use react-navigation
, just like react-router
, they need to include a dynamic variable in their navigation
prop in order to cause re-render when changing the routes. And it will make the PureComponent
useless because it simply does a shallow check against all props. You need to implement your own shouldComponentUpdate()
and validate any prop
but the navigation
. Just that easy. One line is fine:
1 | shouldComponentUpdate(nextProp) { |
5.2 For the patterns
If you are not using either HOC (high order component) or render props that much and the community seems to talk about them every day. There is nothing wrong because you will use them when you need them, you just need to know when to use them, I feel like at least for me:
- HOC is more suitable for adding a reusable feature like I need to add a feature for a TextInput, you just put them in a HOC, and wrap the TextInput. I just want a feature, nothing changes.
- You can use a factory method along with HOC to generate a different version of the same component like I’d like to generate 5 different
ListComponent
, they are all the same, the only differences are they focus on a different piece of redux state. - And you might want to give your HOC a
displayName
for a better debugging experience.
- You can use a factory method along with HOC to generate a different version of the same component like I’d like to generate 5 different
- Render Props is useful when you try to encapsulate a logic and letting the children decide how to render based on a changed input. This is why you see its lambda-like syntax because it will pass the processing result to the children.
- Something like an
Auth
component, you do the authentication inside and pass the result to the children to do a rendering.
- Something like an
An important fact is to remember:
the JSX element in React is not only for showing the UI. You can use them to declarative construct your logic.
And only after you accept this, you will feel better when you see things like render props
, otherwise, it just seems very annoying.
6. React navigation
Actually, the v2 update seems pretty good. I know this library has some pretty bad reputation. But actually not that bad. It works quite well. Just some notes:
Navigators can be nested. Which mean, if you have the main screen which is 3 bottom tabs, and one of the tabs contains 3 top tab bar. You need to do something like this.
1
2
3
4
5
6
7
8
9
10
11
12
13const ListTab = createMaterialTopTabNavigator(
{
INBOX: pickListScreen(Category.INBOX),
TODAY: pickListScreen(Category.TODAY),
TOMORROW: pickListScreen(Category.TOMORROW),
}
)
const MainScreen = createBottomTabNavigator(
{
Home: HomeScreen
List: ListTab,
})And if you want to navigate to another from
HomeScreen
, you need to create astackNavigator
around them and replace the aboveHomeScreen
with it.No, there is NO header, it’s ONLY from stack navigator unless you wrap your screen in a stack navigator, you won’t get a
header
.If you want to remove the header shadow:
1
2
3
4
5
6headerStyle:{
// iOS
borderBottomWidth:0,
// Android
elevation:0
}
7. How to choose an open source project
- Know your use case!
- Inspect the source code, sometimes it is quite simple and maybe you just want a different API signature.
- See the stars, see how much attention it gets. Less attention may end in no support soon.
- Check whether the issues are urgent.
- Check the PR, if lots of PR are unmerged, something must happen there.
- Check the commits. Previously, when I check
sails.js
‘s commit message. It blew my mind by seeing the team has a huge interest in fixing theirreadme.md
or other docs without pushing meaningful codes for months…
8. For CLI
If you are coming from web world, you need to know that create-react-native-app
is not the create-react-app
equivalent. It’s a solution provided by Expo and based on the official react-native-cli
, it hides those native project away from you. They provide other pretty good tooling around react native.
But if you want more control over your project, something like tweaking your react native Android and iOS project, I highly suggest you use the official react-native-cli
. Still, one simple command, react-native init ProjectName
, and you are good to go.
Because whenever you need to add some native code, which sounds pretty normal for react native, you need to eject
your create-react-native-app
as well.
9. For Typescript
9.1 About setup
1 | react-native init ProjectName --template typescript |
9.2 What about Hot reloading not work
First, confirm you have live reloading turned off and hot reloading turned on. Then, make your root component a class component.
I’ve run into an issue that tsconfig.json
causes the trouble, where after I removed all the comments… The hot reloading finally works.
9.3 Start typing
For stateless functional component
1 | import React from 'react' |
For state component
1 | class My extends React.Component<IMyProp, IMyState> |
For redux
1 | // need to install @types/redux first |
10. End
Hope it helps.
Follow me (albertgao) on twitter, if you want to hear more about my interesting ideas.
Thanks for reading!