forked from facebook/react-native
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.html
More file actions
379 lines (358 loc) · 82.3 KB
/
index.html
File metadata and controls
379 lines (358 loc) · 82.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
<!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta http-equiv="X-UA-Compatible" content="IE=edge"/><title>Performance · React Native</title><meta name="viewport" content="width=device-width"/><meta name="generator" content="Docusaurus"/><meta name="description" content="A compelling reason for using React Native instead of WebView-based tools is to achieve 60 frames per second and a native look and feel to your apps. Where possible, we would like for React Native to do the right thing and help you to focus on your app instead of performance optimization, but there are areas where we're not quite there yet, and others where React Native (similar to writing native code directly) cannot possibly determine the best way to optimize for you and so manual intervention will be necessary. We try our best to deliver buttery-smooth UI performance by default, but sometimes that isn't possible."/><meta name="docsearch:version" content="0.13"/><meta name="docsearch:language" content="en"/><meta property="og:title" content="Performance · React Native"/><meta property="og:type" content="website"/><meta property="og:url" content="https://reactnative.dev/"/><meta property="og:description" content="A compelling reason for using React Native instead of WebView-based tools is to achieve 60 frames per second and a native look and feel to your apps. Where possible, we would like for React Native to do the right thing and help you to focus on your app instead of performance optimization, but there are areas where we're not quite there yet, and others where React Native (similar to writing native code directly) cannot possibly determine the best way to optimize for you and so manual intervention will be necessary. We try our best to deliver buttery-smooth UI performance by default, but sometimes that isn't possible."/><meta property="og:image" content="https://reactnative.dev/img/logo-og.png"/><meta name="twitter:card" content="summary"/><meta name="twitter:image" content="https://reactnative.dev/img/logo-og.png"/><link rel="shortcut icon" href="/img/favicon.ico"/><link rel="stylesheet" href="https://cdn.jsdelivr.net/docsearch.js/1/docsearch.min.css"/><link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/solarized-dark.min.css"/><link rel="alternate" type="application/atom+xml" href="https://reactnative.dev/blog/atom.xml" title="React Native Blog ATOM Feed"/><link rel="alternate" type="application/rss+xml" href="https://reactnative.dev/blog/feed.xml" title="React Native Blog RSS Feed"/><script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-41298772-2', 'auto');
ga('send', 'pageview');
</script><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/focus-visible@5.0.2/dist/focus-visible.min.js"></script><script type="text/javascript" src="https://snack.expo.io/embed.js"></script><script type="text/javascript" src="https://platform.twitter.com/widgets.js"></script><script type="text/javascript" src="https://buttons.github.io/buttons.js"></script><script type="text/javascript" src="/js/codeblocks.js"></script><script type="text/javascript" src="/js/tabs.js"></script><script type="text/javascript" src="/js/docs-rating.js"></script><script type="text/javascript" src="/js/announcement.js"></script><script src="https://unpkg.com/vanilla-back-to-top@7.1.14/dist/vanilla-back-to-top.min.js"></script><script>
document.addEventListener('DOMContentLoaded', function() {
addBackToTop(
{"zIndex":100}
)
});
</script><script src="/js/scrollSpy.js"></script><link rel="stylesheet" href="/css/prism.css"/><link rel="stylesheet" href="/css/main.css"/><script src="/js/codetabs.js"></script></head><body class="sideNavVisible separateOnPageNav"><div class="fixedHeaderContainer"><div class="headerWrapper wrapper"><header><a href="/"><img class="logo" src="/img/header_logo.svg" alt="React Native"/><h2 class="headerTitleWithLogo">React Native</h2></a><a href="/versions"><h3>0.13</h3></a><div class="navigationWrapper navigationSlider"><nav class="slidingNav"><ul class="nav-site nav-site-internal"><li class="siteNavGroupActive"><a href="/docs/0.13/getting-started" target="_self">Docs</a></li><li class="siteNavGroupActive"><a href="/docs/0.13/components-and-apis" target="_self">Components</a></li><li class=""><a href="/docs/0.13/accessibilityinfo" target="_self">API</a></li><li class=""><a href="/help" target="_self">Community</a></li><li class=""><a href="/blog/" target="_self">Blog</a></li><li class="navSearchWrapper reactNavSearchWrapper"><input type="text" id="search_input_react" placeholder="Search" title="Search"/></li><li class=""><a href="https://github.com/facebook/react-native" target="_self">GitHub</a></li></ul></nav></div></header></div></div><div class="navPusher"><div class="docMainWrapper wrapper"><div class="docsNavContainer" id="docsNav"><nav class="toc"><div class="toggleNav"><section class="navWrapper wrapper"><div class="navBreadcrumb wrapper"><div class="navToggle" id="navToggler"><div class="hamburger-menu"><div class="line1"></div><div class="line2"></div><div class="line3"></div></div></div><h2><i>›</i><span>Guides</span></h2><div class="tocToggler" id="tocToggler"><i class="icon-toc"></i></div></div><div class="navGroups"><div class="navGroup"><h3 class="navGroupCategoryTitle collapsible">The Basics<span class="arrow"><svg width="24" height="24" viewBox="0 0 24 24"><path fill="#565656" d="M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z"></path><path d="M0 0h24v24H0z" fill="none"></path></svg></span></h3><ul class="hide"><li class="navListItem"><a class="navItem" href="/docs/0.13/getting-started">Getting Started</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/tutorial">Learn the Basics</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/props">Props</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/state">State</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/style">Style</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/height-and-width">Height and Width</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/flexbox">Layout with Flexbox</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/handling-text-input">Handling Text Input</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/handling-touches">Handling Touches</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/using-a-scrollview">Using a ScrollView</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/using-a-listview">Using List Views</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/network">Networking</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/more-resources">More Resources</a></li></ul></div><div class="navGroup"><h3 class="navGroupCategoryTitle collapsible">Guides<span class="arrow"><svg width="24" height="24" viewBox="0 0 24 24"><path fill="#565656" d="M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z"></path><path d="M0 0h24v24H0z" fill="none"></path></svg></span></h3><ul class="hide"><li class="navListItem"><a class="navItem" href="/docs/0.13/components-and-apis">Components and APIs</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/platform-specific-code">Platform Specific Code</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/navigation">Navigating Between Screens</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/images">Images</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/animations">Animations</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/accessibility">Accessibility</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/improvingux">Improving User Experience</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/timers">Timers</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/debugging">Debugging</a></li><li class="navListItem navListItemActive"><a class="navItem" href="/docs/0.13/performance">Performance</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/gesture-responder-system">Gesture Responder System</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/javascript-environment">JavaScript Environment</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/direct-manipulation">Direct Manipulation</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/colors">Color Reference</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/integration-with-existing-apps">Integration with Existing Apps</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/running-on-device">Running On Device</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/upgrading">Upgrading to new React Native versions</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/troubleshooting">Troubleshooting</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/native-modules-setup">Native Modules Setup</a></li></ul></div><div class="navGroup"><h3 class="navGroupCategoryTitle collapsible">Guides (iOS)<span class="arrow"><svg width="24" height="24" viewBox="0 0 24 24"><path fill="#565656" d="M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z"></path><path d="M0 0h24v24H0z" fill="none"></path></svg></span></h3><ul class="hide"><li class="navListItem"><a class="navItem" href="/docs/0.13/native-modules-ios">Native Modules</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/native-components-ios">Native UI Components</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/linking-libraries-ios">Linking Libraries</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/running-on-simulator-ios">Running On Simulator</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/communication-ios">Communication between native and React Native</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/building-for-tv">Building For TV Devices</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/app-extensions">App Extensions</a></li></ul></div><div class="navGroup"><h3 class="navGroupCategoryTitle collapsible">Guides (Android)<span class="arrow"><svg width="24" height="24" viewBox="0 0 24 24"><path fill="#565656" d="M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z"></path><path d="M0 0h24v24H0z" fill="none"></path></svg></span></h3><ul class="hide"><li class="navListItem"><a class="navItem" href="/docs/0.13/native-modules-android">Native Modules</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/native-components-android">Native UI Components</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/headless-js-android">Headless JS</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/signed-apk-android">Publishing to Google Play Store</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/removing-default-permissions">Removing Default Permissions</a></li></ul></div><div class="navGroup"><h3 class="navGroupCategoryTitle collapsible">Components<span class="arrow"><svg width="24" height="24" viewBox="0 0 24 24"><path fill="#565656" d="M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z"></path><path d="M0 0h24v24H0z" fill="none"></path></svg></span></h3><ul class="hide"><li class="navListItem"><a class="navItem" href="/docs/0.13/datepickerios">DatePickerIOS</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/drawerlayoutandroid">DrawerLayoutAndroid</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/image">Image</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/modal">Modal</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/navigator">Navigator</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/navigatorios">NavigatorIOS</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/progressbarandroid">ProgressBarAndroid</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/progressviewios">ProgressViewIOS</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/scrollview">ScrollView</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/segmentedcontrolios">SegmentedControlIOS</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/switch">Switch</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/tabbarios">TabBarIOS</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/tabbarios-item">TabBarIOS.Item</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/text">Text</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/textinput">TextInput</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/toolbarandroid">ToolbarAndroid</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/touchablehighlight">TouchableHighlight</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/touchablenativefeedback">TouchableNativeFeedback</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/touchableopacity">TouchableOpacity</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/touchablewithoutfeedback">TouchableWithoutFeedback</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/viewpagerandroid">ViewPagerAndroid</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/webview">WebView</a></li></ul></div><div class="navGroup"><h3 class="navGroupCategoryTitle collapsible">APIs<span class="arrow"><svg width="24" height="24" viewBox="0 0 24 24"><path fill="#565656" d="M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z"></path><path d="M0 0h24v24H0z" fill="none"></path></svg></span></h3><ul class="hide"><li class="navListItem"><a class="navItem" href="/docs/0.13/actionsheetios">ActionSheetIOS</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/animated">Animated</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/cameraroll">CameraRoll</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/dimensions">Dimensions</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/easing">Easing</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/geolocation">Geolocation</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/imagepickerios">ImagePickerIOS</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/image-style-props">Image Style Props</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/interactionmanager">InteractionManager</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/layout-props">Layout Props</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/layoutanimation">LayoutAnimation</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/netinfo">NetInfo</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/pixelratio">PixelRatio</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/pushnotificationios">PushNotificationIOS</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/settings">Settings</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/statusbarios">StatusBarIOS</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/stylesheet">StyleSheet</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/text-style-props">Text Style Props</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/toastandroid">ToastAndroid</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/transforms">Transforms</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/vibrationios">VibrationIOS</a></li><li class="navListItem"><a class="navItem" href="/docs/0.13/view-style-props">View Style Props</a></li></ul></div></div></section></div><script>
var coll = document.getElementsByClassName('collapsible');
var checkActiveCategory = true;
for (var i = 0; i < coll.length; i++) {
var links = coll[i].nextElementSibling.getElementsByTagName('*');
if (checkActiveCategory){
for (var j = 0; j < links.length; j++) {
if (links[j].classList.contains('navListItemActive')){
coll[i].nextElementSibling.classList.toggle('hide');
coll[i].childNodes[1].classList.toggle('rotate');
checkActiveCategory = false;
break;
}
}
}
coll[i].addEventListener('click', function() {
var arrow = this.childNodes[1];
arrow.classList.toggle('rotate');
var content = this.nextElementSibling;
content.classList.toggle('hide');
});
}
document.addEventListener('DOMContentLoaded', function() {
createToggler('#navToggler', '#docsNav', 'docsSliderActive');
createToggler('#tocToggler', 'body', 'tocActive');
var headings = document.querySelector('.toc-headings');
headings && headings.addEventListener('click', function(event) {
var el = event.target;
while(el !== headings){
if (el.tagName === 'A') {
document.body.classList.remove('tocActive');
break;
} else{
el = el.parentNode;
}
}
}, false);
function createToggler(togglerSelector, targetSelector, className) {
var toggler = document.querySelector(togglerSelector);
var target = document.querySelector(targetSelector);
if (!toggler) {
return;
}
toggler.onclick = function(event) {
event.preventDefault();
target.classList.toggle(className);
};
}
});
</script></nav></div><div class="container mainContainer docsContainer"><div class="wrapper"><div class="post"><header class="postHeader"><a class="edit-page-link button" href="https://github.com/facebook/react-native-website/blob/master/docs/performance.md" target="_blank" rel="noreferrer noopener">Edit</a><h1 id="__docusaurus" class="postHeaderTitle">Performance</h1></header><article><div><span><p>A compelling reason for using React Native instead of WebView-based tools is to achieve 60 frames per second and a native look and feel to your apps. Where possible, we would like for React Native to do the right thing and help you to focus on your app instead of performance optimization, but there are areas where we're not quite there yet, and others where React Native (similar to writing native code directly) cannot possibly determine the best way to optimize for you and so manual intervention will be necessary. We try our best to deliver buttery-smooth UI performance by default, but sometimes that isn't possible.</p>
<p>This guide is intended to teach you some basics to help you to <a href="/docs/0.13/performance#profiling">troubleshoot performance issues</a>, as well as discuss <a href="/docs/0.13/performance#common-sources-of-performance-problems">common sources of problems and their suggested solutions</a>.</p>
<h2><a class="anchor" aria-hidden="true" id="what-you-need-to-know-about-frames"></a><a href="#what-you-need-to-know-about-frames" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>What you need to know about frames</h2>
<p>Your grandparents' generation called movies <a href="https://www.youtube.com/watch?v=F1i40rnpOsA">"moving pictures"</a> for a reason: realistic motion in video is an illusion created by quickly changing static images at a consistent speed. We refer to each of these images as frames. The number of frames that is displayed each second has a direct impact on how smooth and ultimately life-like a video (or user interface) seems to be. iOS devices display 60 frames per second, which gives you and the UI system about 16.67ms to do all of the work needed to generate the static image (frame) that the user will see on the screen for that interval. If you are unable to do the work necessary to generate that frame within the allotted 16.67ms, then you will "drop a frame" and the UI will appear unresponsive.</p>
<p>Now to confuse the matter a little bit, open up the developer menu in your app and toggle <code>Show Perf Monitor</code>. You will notice that there are two different frame rates.</p>
<p><img src="/docs/assets/PerfUtil.png" alt=""></p>
<h3><a class="anchor" aria-hidden="true" id="js-frame-rate-javascript-thread"></a><a href="#js-frame-rate-javascript-thread" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>JS frame rate (JavaScript thread)</h3>
<p>For most React Native applications, your business logic will run on the JavaScript thread. This is where your React application lives, API calls are made, touch events are processed, etc... Updates to native-backed views are batched and sent over to the native side at the end of each iteration of the event loop, before the frame deadline (if all goes well). If the JavaScript thread is unresponsive for a frame, it will be considered a dropped frame. For example, if you were to call <code>this.setState</code> on the root component of a complex application and it resulted in re-rendering computationally expensive component subtrees, it's conceivable that this might take 200ms and result in 12 frames being dropped. Any animations controlled by JavaScript would appear to freeze during that time. If anything takes longer than 100ms, the user will feel it.</p>
<p>This often happens during <code>Navigator</code> transitions: when you push a new route, the JavaScript thread needs to render all of the components necessary for the scene in order to send over the proper commands to the native side to create the backing views. It's common for the work being done here to take a few frames and cause <a href="http://jankfree.org/">jank</a> because the transition is controlled by the JavaScript thread. Sometimes components will do additional work on <code>componentDidMount</code>, which might result in a second stutter in the transition.</p>
<p>Another example is responding to touches: if you are doing work across multiple frames on the JavaScript thread, you might notice a delay in responding to <code>TouchableOpacity</code>, for example. This is because the JavaScript thread is busy and cannot process the raw touch events sent over from the main thread. As a result, <code>TouchableOpacity</code> cannot react to the touch events and command the native view to adjust its opacity.</p>
<h3><a class="anchor" aria-hidden="true" id="ui-frame-rate-main-thread"></a><a href="#ui-frame-rate-main-thread" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>UI frame rate (main thread)</h3>
<p>Many people have noticed that performance of <code>NavigatorIOS</code> is better out of the box than <code>Navigator</code>. The reason for this is that the animations for the transitions are done entirely on the main thread, and so they are not interrupted by frame drops on the JavaScript thread.</p>
<p>Similarly, you can happily scroll up and down through a <code>ScrollView</code> when the JavaScript thread is locked up because the <code>ScrollView</code> lives on the main thread. The scroll events are dispatched to the JS thread, but their receipt is not necessary for the scroll to occur.</p>
<h2><a class="anchor" aria-hidden="true" id="common-sources-of-performance-problems"></a><a href="#common-sources-of-performance-problems" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Common sources of performance problems</h2>
<h3><a class="anchor" aria-hidden="true" id="running-in-development-mode-devtrue"></a><a href="#running-in-development-mode-devtrue" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Running in development mode (<code>dev=true</code>)</h3>
<p>JavaScript thread performance suffers greatly when running in dev mode. This is unavoidable: a lot more work needs to be done at runtime to provide you with good warnings and error messages, such as validating propTypes and various other assertions. Always make sure to test performance in <a href="/docs/0.13/running-on-device#building-your-app-for-production">release builds</a>.</p>
<h3><a class="anchor" aria-hidden="true" id="using-consolelog-statements"></a><a href="#using-consolelog-statements" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Using <code>console.log</code> statements</h3>
<p>When running a bundled app, these statements can cause a big bottleneck in the JavaScript thread. This includes calls from debugging libraries such as <a href="https://github.com/evgenyrodionov/redux-logger">redux-logger</a>, so make sure to remove them before bundling. You can also use this <a href="https://babeljs.io/docs/plugins/transform-remove-console/">babel plugin</a> that removes all the <code>console.*</code> calls. You need to install it first with <code>npm i babel-plugin-transform-remove-console --save-dev</code>, and then edit the <code>.babelrc</code> file under your project directory like this:</p>
<pre><code class="hljs css language-jsxon">{
<span class="hljs-attr">"env"</span>: {
<span class="hljs-attr">"production"</span>: {
<span class="hljs-attr">"plugins"</span>: [<span class="hljs-string">"transform-remove-console"</span>]
}
}
}
</code></pre>
<p>This will automatically remove all <code>console.*</code> calls in the release (production) versions of your project.</p>
<h3><a class="anchor" aria-hidden="true" id="listview-initial-rendering-is-too-slow-or-scroll-performance-is-bad-for-large-lists"></a><a href="#listview-initial-rendering-is-too-slow-or-scroll-performance-is-bad-for-large-lists" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a><code>ListView</code> initial rendering is too slow or scroll performance is bad for large lists</h3>
<p>Use the new <a href="/docs/0.13/flatlist"><code>FlatList</code></a> or <a href="/docs/0.13/sectionlist"><code>SectionList</code></a> component instead. Besides simplifying the API, the new list components also have significant performance enhancements, the main one being nearly constant memory usage for any number of rows.</p>
<p>If your <a href="/docs/0.13/flatlist"><code>FlatList</code></a> is rendering slow, be sure that you've implemented <a href="/docs/flatlist.html#getitemlayout"><code>getItemLayout</code></a> to optimize rendering speed by skipping measurement of the rendered items.</p>
<h3><a class="anchor" aria-hidden="true" id="js-fps-plunges-when-re-rendering-a-view-that-hardly-changes"></a><a href="#js-fps-plunges-when-re-rendering-a-view-that-hardly-changes" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>JS FPS plunges when re-rendering a view that hardly changes</h3>
<p>If you are using a ListView, you must provide a <code>rowHasChanged</code> function that can reduce a lot of work by quickly determining whether or not a row needs to be re-rendered. If you are using immutable data structures, this would only need to be a reference equality check.</p>
<p>Similarly, you can implement <code>shouldComponentUpdate</code> and indicate the exact conditions under which you would like the component to re-render. If you write pure components (where the return value of the render function is entirely dependent on props and state), you can leverage PureComponent to do this for you. Once again, immutable data structures are useful to keep this fast -- if you have to do a deep comparison of a large list of objects, it may be that re-rendering your entire component would be quicker, and it would certainly require less code.</p>
<h3><a class="anchor" aria-hidden="true" id="dropping-js-thread-fps-because-of-doing-a-lot-of-work-on-the-javascript-thread-at-the-same-time"></a><a href="#dropping-js-thread-fps-because-of-doing-a-lot-of-work-on-the-javascript-thread-at-the-same-time" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Dropping JS thread FPS because of doing a lot of work on the JavaScript thread at the same time</h3>
<p>"Slow Navigator transitions" is the most common manifestation of this, but there are other times this can happen. Using InteractionManager can be a good approach, but if the user experience cost is too high to delay work during an animation, then you might want to consider LayoutAnimation.</p>
<p>The Animated API currently calculates each keyframe on-demand on the JavaScript thread unless you <a href="/blog/2017/02/14/using-native-driver-for-animated.html#how-do-i-use-this-in-my-app">set <code>useNativeDriver: true</code></a>, while LayoutAnimation leverages Core Animation and is unaffected by JS thread and main thread frame drops.</p>
<p>One case where I have used this is for animating in a modal (sliding down from top and fading in a translucent overlay) while initializing and perhaps receiving responses for several network requests, rendering the contents of the modal, and updating the view where the modal was opened from. See the Animations guide for more information about how to use LayoutAnimation.</p>
<p>Caveats:</p>
<ul>
<li>LayoutAnimation only works for fire-and-forget animations ("static" animations) -- if it must be interruptible, you will need to use <code>Animated</code>.</li>
</ul>
<h3><a class="anchor" aria-hidden="true" id="moving-a-view-on-the-screen-scrolling-translating-rotating-drops-ui-thread-fps"></a><a href="#moving-a-view-on-the-screen-scrolling-translating-rotating-drops-ui-thread-fps" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Moving a view on the screen (scrolling, translating, rotating) drops UI thread FPS</h3>
<p>This is especially true when you have text with a transparent background positioned on top of an image, or any other situation where alpha compositing would be required to re-draw the view on each frame. You will find that enabling <code>shouldRasterizeIOS</code> or <code>renderToHardwareTextureAndroid</code> can help with this significantly.</p>
<p>Be careful not to overuse this or your memory usage could go through the roof. Profile your performance and memory usage when using these props. If you don't plan to move a view anymore, turn this property off.</p>
<h3><a class="anchor" aria-hidden="true" id="animating-the-size-of-an-image-drops-ui-thread-fps"></a><a href="#animating-the-size-of-an-image-drops-ui-thread-fps" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Animating the size of an image drops UI thread FPS</h3>
<p>On iOS, each time you adjust the width or height of an Image component it is re-cropped and scaled from the original image. This can be very expensive, especially for large images. Instead, use the <code>transform: [{scale}]</code> style property to animate the size. An example of when you might do this is when you tap an image and zoom it in to full screen.</p>
<h3><a class="anchor" aria-hidden="true" id="my-touchablex-view-isnt-very-responsive"></a><a href="#my-touchablex-view-isnt-very-responsive" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>My TouchableX view isn't very responsive</h3>
<p>Sometimes, if we do an action in the same frame that we are adjusting the opacity or highlight of a component that is responding to a touch, we won't see that effect until after the <code>onPress</code> function has returned. If <code>onPress</code> does a <code>setState</code> that results in a lot of work and a few frames dropped, this may occur. A solution to this is to wrap any action inside of your <code>onPress</code> handler in <code>requestAnimationFrame</code>:</p>
<pre><code class="hljs css language-jsx"><span class="token function">handleOnPress</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token function">requestAnimationFrame</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">doExpensiveAction</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre>
<h3><a class="anchor" aria-hidden="true" id="slow-navigator-transitions"></a><a href="#slow-navigator-transitions" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Slow navigator transitions</h3>
<p>As mentioned above, <code>Navigator</code> animations are controlled by the JavaScript thread. Imagine the "push from right" scene transition: each frame, the new scene is moved from the right to left, starting offscreen (let's say at an x-offset of 320) and ultimately settling when the scene sits at an x-offset of</p>
<ol>
<li>Each frame during this transition, the JavaScript thread needs to send a new x-offset to the main thread. If the JavaScript thread is locked up, it cannot do this and so no update occurs on that frame and the animation stutters.</li>
</ol>
<p>One solution to this is to allow for JavaScript-based animations to be offloaded to the main thread. If we were to do the same thing as in the above example with this approach, we might calculate a list of all x-offsets for the new scene when we are starting the transition and send them to the main thread to execute in an optimized way. Now that the JavaScript thread is freed of this responsibility, it's not a big deal if it drops a few frames while rendering the scene -- you probably won't even notice because you will be too distracted by the pretty transition.</p>
<p>Solving this is one of the main goals behind the new <a href="/docs/0.13/navigation">React Navigation</a> library. The views in React Navigation use native components and the <a href="/docs/0.13/animated"><code>Animated</code></a> library to deliver 60 FPS animations that are run on the native thread.</p>
<h2><a class="anchor" aria-hidden="true" id="profiling"></a><a href="#profiling" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Profiling</h2>
<p>Use the built-in profiler to get detailed information about work done in the JavaScript thread and main thread side-by-side. Access it by selecting Perf Monitor from the Debug menu.</p>
<p>For iOS, Instruments is an invaluable tool, and on Android you should learn to use <a href="/docs/0.13/performance#profiling-android-ui-performance-with-systrace"><code>systrace</code></a>.</p>
<p>But first, <a href="/docs/0.13/performance#running-in-development-mode-dev-true"><strong>make sure that Development Mode is OFF!</strong></a> You should see <code>__DEV__ === false, development-level warning are OFF, performance optimizations are ON</code> in your application logs.</p>
<p>Another way to profile JavaScript is to use the Chrome profiler while debugging. This won't give you accurate results as the code is running in Chrome but will give you a general idea of where bottlenecks might be. Run the profiler under Chrome's <code>Performance</code> tab. A flame graph will appear under <code>User Timing</code>. To view more details in tabular format, click at the <code>Bottom Up</code> tab below and then select <code>DedicatedWorker Thread</code> at the top left menu.</p>
<h3><a class="anchor" aria-hidden="true" id="profiling-android-ui-performance-with-systrace"></a><a href="#profiling-android-ui-performance-with-systrace" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Profiling Android UI Performance with <code>systrace</code></h3>
<p>Android supports 10k+ different phones and is generalized to support software rendering: the framework architecture and need to generalize across many hardware targets unfortunately means you get less for free relative to iOS. But sometimes, there are things you can improve -- and many times it's not native code's fault at all!</p>
<p>The first step for debugging this jank is to answer the fundamental question of where your time is being spent during each 16ms frame. For that, we'll be using a standard Android profiling tool called <code>systrace</code>.</p>
<p><code>systrace</code> is a standard Android marker-based profiling tool (and is installed when you install the Android platform-tools package). Profiled code blocks are surrounded by start/end markers which are then visualized in a colorful chart format. Both the Android SDK and React Native framework provide standard markers that you can visualize.</p>
<h4><a class="anchor" aria-hidden="true" id="1-collecting-a-trace"></a><a href="#1-collecting-a-trace" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>1. Collecting a trace</h4>
<p>First, connect a device that exhibits the stuttering you want to investigate to your computer via USB and get it to the point right before the navigation/animation you want to profile. Run <code>systrace</code> as follows:</p>
<pre><code class="hljs">$ <span class="hljs-symbol"><path_to_android_sdk></span>/platform-tools/systrace/systrace.<span class="hljs-keyword">py</span> --time=<span class="hljs-number">10</span> -<span class="hljs-keyword">o</span> trace.html sched gfx <span class="hljs-keyword">view</span> -<span class="hljs-keyword">a</span> <span class="hljs-symbol"><your_package_name></span>
</code></pre>
<p>A quick breakdown of this command:</p>
<ul>
<li><code>time</code> is the length of time the trace will be collected in seconds</li>
<li><code>sched</code>, <code>gfx</code>, and <code>view</code> are the android SDK tags (collections of markers) we care about: <code>sched</code> gives you information about what's running on each core of your phone, <code>gfx</code> gives you graphics info such as frame boundaries, and <code>view</code> gives you information about measure, layout, and draw passes</li>
<li><code>-a <your_package_name></code> enables app-specific markers, specifically the ones built into the React Native framework. <code>your_package_name</code> can be found in the <code>AndroidManifest.xml</code> of your app and looks like <code>com.example.app</code></li>
</ul>
<p>Once the trace starts collecting, perform the animation or interaction you care about. At the end of the trace, systrace will give you a link to the trace which you can open in your browser.</p>
<h4><a class="anchor" aria-hidden="true" id="2-reading-the-trace"></a><a href="#2-reading-the-trace" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>2. Reading the trace</h4>
<p>After opening the trace in your browser (preferably Chrome), you should see something like this:</p>
<p><img src="/docs/assets/SystraceExample.png" alt="Example"></p>
<blockquote>
<p><strong>HINT</strong>: Use the WASD keys to strafe and zoom</p>
</blockquote>
<p>If your trace .html file isn't opening correctly, check your browser console for the following:</p>
<p><img src="/docs/assets/ObjectObserveError.png" alt="ObjectObserveError"></p>
<p>Since <code>Object.observe</code> was deprecated in recent browsers, you may have to open the file from the Google Chrome Tracing tool. You can do so by:</p>
<ul>
<li>Opening tab in chrome <a href="chrome://tracing">chrome://tracing</a></li>
<li>Selecting load</li>
<li>Selecting the html file generated from the previous command.</li>
</ul>
<blockquote>
<p><strong>Enable VSync highlighting</strong></p>
<p>Check this checkbox at the top right of the screen to highlight the 16ms frame boundaries:</p>
<p><img src="/docs/assets/SystraceHighlightVSync.png" alt="Enable VSync Highlighting"></p>
<p>You should see zebra stripes as in the screenshot above. If you don't, try profiling on a different device: Samsung has been known to have issues displaying vsyncs while the Nexus series is generally pretty reliable.</p>
</blockquote>
<h4><a class="anchor" aria-hidden="true" id="3-find-your-process"></a><a href="#3-find-your-process" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>3. Find your process</h4>
<p>Scroll until you see (part of) the name of your package. In this case, I was profiling <code>com.facebook.adsmanager</code>, which shows up as <code>book.adsmanager</code> because of silly thread name limits in the kernel.</p>
<p>On the left side, you'll see a set of threads which correspond to the timeline rows on the right. There are a few threads we care about for our purposes: the UI thread (which has your package name or the name UI Thread), <code>mqt_js</code>, and <code>mqt_native_modules</code>. If you're running on Android 5+, we also care about the Render Thread.</p>
<ul>
<li><p><strong>UI Thread.</strong> This is where standard android measure/layout/draw happens. The thread name on the right will be your package name (in my case book.adsmanager) or UI Thread. The events that you see on this thread should look something like this and have to do with <code>Choreographer</code>, <code>traversals</code>, and <code>DispatchUI</code>:</p>
<p><img src="/docs/assets/SystraceUIThreadExample.png" alt="UI Thread Example"></p></li>
<li><p><strong>JS Thread.</strong> This is where JavaScript is executed. The thread name will be either <code>mqt_js</code> or <code><...></code> depending on how cooperative the kernel on your device is being. To identify it if it doesn't have a name, look for things like <code>JSCall</code>, <code>Bridge.executeJSCall</code>, etc:</p>
<p><img src="/docs/assets/SystraceJSThreadExample.png" alt="JS Thread Example"></p></li>
<li><p><strong>Native Modules Thread.</strong> This is where native module calls (e.g. the <code>UIManager</code>) are executed. The thread name will be either <code>mqt_native_modules</code> or <code><...></code>. To identify it in the latter case, look for things like <code>NativeCall</code>, <code>callJavaModuleMethod</code>, and <code>onBatchComplete</code>:</p>
<p><img src="/docs/assets/SystraceNativeModulesThreadExample.png" alt="Native Modules Thread Example"></p></li>
<li><p><strong>Bonus: Render Thread.</strong> If you're using Android L (5.0) and up, you will also have a render thread in your application. This thread generates the actual OpenGL commands used to draw your UI. The thread name will be either <code>RenderThread</code> or <code><...></code>. To identify it in the latter case, look for things like <code>DrawFrame</code> and <code>queueBuffer</code>:</p>
<p><img src="/docs/assets/SystraceRenderThreadExample.png" alt="Render Thread Example"></p></li>
</ul>
<h4><a class="anchor" aria-hidden="true" id="identifying-a-culprit"></a><a href="#identifying-a-culprit" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Identifying a culprit</h4>
<p>A smooth animation should look something like the following:</p>
<p><img src="/docs/assets/SystraceWellBehaved.png" alt="Smooth Animation"></p>
<p>Each change in color is a frame -- remember that in order to display a frame, all our UI work needs to be done by the end of that 16ms period. Notice that no thread is working close to the frame boundary. An application rendering like this is rendering at 60 FPS.</p>
<p>If you noticed chop, however, you might see something like this:</p>
<p><img src="/docs/assets/SystraceBadJS.png" alt="Choppy Animation from JS"></p>
<p>Notice that the JS thread is executing almost all the time, and across frame boundaries! This app is not rendering at 60 FPS. In this case, <strong>the problem lies in JS</strong>.</p>
<p>You might also see something like this:</p>
<p><img src="/docs/assets/SystraceBadUI.png" alt="Choppy Animation from UI"></p>
<p>In this case, the UI and render threads are the ones that have work crossing frame boundaries. The UI that we're trying to render on each frame is requiring too much work to be done. In this case, <strong>the problem lies in the native views being rendered</strong>.</p>
<p>At this point, you'll have some very helpful information to inform your next steps.</p>
<h4><a class="anchor" aria-hidden="true" id="resolving-javascript-issues"></a><a href="#resolving-javascript-issues" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Resolving JavaScript issues</h4>
<p>If you identified a JS problem, look for clues in the specific JS that you're executing. In the scenario above, we see <code>RCTEventEmitter</code> being called multiple times per frame. Here's a zoom-in of the JS thread from the trace above:</p>
<p><img src="/docs/assets/SystraceBadJS2.png" alt="Too much JS"></p>
<p>This doesn't seem right. Why is it being called so often? Are they actually different events? The answers to these questions will probably depend on your product code. And many times, you'll want to look into <a href="https://reactjs.org/docs/react-component.html#shouldcomponentupdate">shouldComponentUpdate</a>.</p>
<h4><a class="anchor" aria-hidden="true" id="resolving-native-ui-issues"></a><a href="#resolving-native-ui-issues" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Resolving native UI Issues</h4>
<p>If you identified a native UI problem, there are usually two scenarios:</p>
<ol>
<li>the UI you're trying to draw each frame involves too much work on the GPU, or</li>
<li>You're constructing new UI during the animation/interaction (e.g. loading in new content during a scroll).</li>
</ol>
<h5><a class="anchor" aria-hidden="true" id="too-much-gpu-work"></a><a href="#too-much-gpu-work" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Too much GPU work</h5>
<p>In the first scenario, you'll see a trace that has the UI thread and/or Render Thread looking like this:</p>
<p><img src="/docs/assets/SystraceBadUI.png" alt="Overloaded GPU"></p>
<p>Notice the long amount of time spent in <code>DrawFrame</code> that crosses frame boundaries. This is time spent waiting for the GPU to drain its command buffer from the previous frame.</p>
<p>To mitigate this, you should:</p>
<ul>
<li>investigate using <code>renderToHardwareTextureAndroid</code> for complex, static content that is being animated/transformed (e.g. the <code>Navigator</code> slide/alpha animations)</li>
<li>make sure that you are <strong>not</strong> using <code>needsOffscreenAlphaCompositing</code>, which is disabled by default, as it greatly increases the per-frame load on the GPU in most cases.</li>
</ul>
<p>If these don't help and you want to dig deeper into what the GPU is actually doing, you can check out <a href="http://www.androiddocs.com/tools/help/gltracer.html">Tracer for OpenGL ES</a>.</p>
<h5><a class="anchor" aria-hidden="true" id="creating-new-views-on-the-ui-thread"></a><a href="#creating-new-views-on-the-ui-thread" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Creating new views on the UI thread</h5>
<p>In the second scenario, you'll see something more like this:</p>
<p><img src="/docs/assets/SystraceBadCreateUI.png" alt="Creating Views"></p>
<p>Notice that first the JS thread thinks for a bit, then you see some work done on the native modules thread, followed by an expensive traversal on the UI thread.</p>
<p>There isn't a quick way to mitigate this unless you're able to postpone creating new UI until after the interaction, or you are able to simplify the UI you're creating. The react native team is working on an infrastructure level solution for this that will allow new UI to be created and configured off the main thread, allowing the interaction to continue smoothly.</p>
<h2><a class="anchor" aria-hidden="true" id="ram-bundles--inline-requires"></a><a href="#ram-bundles--inline-requires" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>RAM bundles + inline requires</h2>
<p>If you have a large app you may want to consider the Random Access Modules (RAM) bundle format, and using inline requires. This is useful for apps that have a large number of screens which may not ever be opened during a typical usage of the app. Generally it is useful to apps that have large amounts of code that are not needed for a while after startup. For instance the app includes complicated profile screens or lesser used features, but most sessions only involve visiting the main screen of the app for updates. We can optimize the loading of the bundle by using the RAM format and requiring those features and screens inline (when they are actually used).</p>
<h3><a class="anchor" aria-hidden="true" id="loading-javascript"></a><a href="#loading-javascript" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Loading JavaScript</h3>
<p>Before react-native can execute JS code, that code must be loaded into memory and parsed. With a standard bundle if you load a 50mb bundle, all 50mb must be loaded and parsed before any of it can be executed. The optimization behind RAM bundles is that you can load only the portion of the 50mb that you actually need at startup, and progressively load more of the bundle as those sections are needed.</p>
<h3><a class="anchor" aria-hidden="true" id="inline-requires"></a><a href="#inline-requires" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Inline Requires</h3>
<p>Inline requires delay the requiring of a module or file until that file is actually needed. A basic example would look like this:</p>
<h4><a class="anchor" aria-hidden="true" id="veryexpensivejs"></a><a href="#veryexpensivejs" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>VeryExpensive.js</h4>
<pre><code class="hljs"><span class="hljs-keyword">import</span> <span class="hljs-type">React</span>, { <span class="hljs-type">Component</span> } from <span class="hljs-symbol">'reac</span>t';
<span class="hljs-keyword">import</span> { <span class="hljs-type">Text</span> } from <span class="hljs-symbol">'react</span>-native';
<span class="hljs-comment">// ... import some very expensive modules</span>
<span class="hljs-comment">// You may want to log at the file level to verify when this is happening</span>
console.log(<span class="hljs-symbol">'VeryExpensive</span> component loaded');
export <span class="hljs-keyword">default</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">VeryExpensive</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Component</span> </span>{
<span class="hljs-comment">// lots and lots of code</span>
render() {
<span class="hljs-keyword">return</span> <<span class="hljs-type">Text</span>><span class="hljs-type">Very</span> <span class="hljs-type">Expensive</span> <span class="hljs-type">Component</span></<span class="hljs-type">Text</span>>;
}
}
</code></pre>
<h4><a class="anchor" aria-hidden="true" id="optimizedjs"></a><a href="#optimizedjs" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Optimized.js</h4>
<pre><code class="hljs"><span class="hljs-keyword">import</span> React, { Component } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> { TouchableOpacity, View, Text } <span class="hljs-keyword">from</span> <span class="hljs-string">'react-native'</span>;
<span class="hljs-keyword">let</span> VeryExpensive = <span class="hljs-literal">null</span>;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Optimized</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Component</span> </span>{
state = { <span class="hljs-attr">needsExpensive</span>: <span class="hljs-literal">false</span> };
didPress = <span class="hljs-function"><span class="hljs-params">()</span> =></span> {
<span class="hljs-keyword">if</span> (VeryExpensive == <span class="hljs-literal">null</span>) {
VeryExpensive = <span class="hljs-built_in">require</span>(<span class="hljs-string">'./VeryExpensive'</span>).default;
}
<span class="hljs-keyword">this</span>.setState(<span class="hljs-function"><span class="hljs-params">()</span> =></span> ({
<span class="hljs-attr">needsExpensive</span>: <span class="hljs-literal">true</span>,
}));
};
render() {
<span class="hljs-keyword">return</span> (
<span class="xml"><span class="hljs-tag"><<span class="hljs-name">View</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">marginTop:</span> <span class="hljs-attr">20</span> }}></span>
<span class="hljs-tag"><<span class="hljs-name">TouchableOpacity</span> <span class="hljs-attr">onPress</span>=<span class="hljs-string">{this.didPress}</span>></span>
<span class="hljs-tag"><<span class="hljs-name">Text</span>></span>Load<span class="hljs-tag"></<span class="hljs-name">Text</span>></span>
<span class="hljs-tag"></<span class="hljs-name">TouchableOpacity</span>></span>
{this.state.needsExpensive ? <span class="hljs-tag"><<span class="hljs-name">VeryExpensive</span> /></span> : null}
<span class="hljs-tag"></<span class="hljs-name">View</span>></span></span>
);
}
}
</code></pre>
<p>Even without the RAM format, inline requires can lead to startup time improvements, because the code within VeryExpensive.js will only execute once it is required for the first time.</p>
<h3><a class="anchor" aria-hidden="true" id="enable-the-ram-format"></a><a href="#enable-the-ram-format" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Enable the RAM format</h3>
<p>On iOS using the RAM format will create a single indexed file that react native will load one module at a time. On Android, by default it will create a set of files for each module. You can force Android to create a single file, like iOS, but using multiple files can be more performant and requires less memory.</p>
<p>Enable the RAM format in Xcode by editing the build phase "Bundle React Native code and images". Before <code>../node_modules/react-native/scripts/react-native-xcode.sh</code> add <code>export BUNDLE_COMMAND="ram-bundle"</code>:</p>
<pre><code class="hljs"><span class="hljs-builtin-name">export</span> <span class="hljs-attribute">BUNDLE_COMMAND</span>=<span class="hljs-string">"ram-bundle"</span>
<span class="hljs-builtin-name">export</span> <span class="hljs-attribute">NODE_BINARY</span>=node
<span class="hljs-built_in">..</span>/node_modules/react-native/scripts/react-native-xcode.sh
</code></pre>
<p>On Android enable the RAM format by editing your <code>android/app/build.gradle</code> file. Before the line <code>apply from: "../../node_modules/react-native/react.gradle"</code> add or amend the <code>project.ext.react</code> block:</p>
<pre><code class="hljs">project<span class="hljs-selector-class">.ext</span><span class="hljs-selector-class">.react</span> = [
bundleCommand: <span class="hljs-string">"ram-bundle"</span>,
]
</code></pre>
<p>Use the following lines on Android if you want to use a single indexed file:</p>
<pre><code class="hljs">project.ext.react = [
<span class="hljs-symbol"> bundleCommand:</span> <span class="hljs-string">"ram-bundle"</span>,
<span class="hljs-symbol"> extraPackagerArgs:</span> [<span class="hljs-string">"--indexed-ram-bundle"</span>]
]
</code></pre>
<blockquote>
<p><strong><em>Note</em></strong>: If you are using <a href="https://github.com/facebook/hermes">Hermes JS Engine</a>, you do not need RAM bundles. When loading the bytecode, <code>mmap</code> ensures that the entire file is not loaded.</p>
</blockquote>
<h3><a class="anchor" aria-hidden="true" id="configure-preloading-and-inline-requires"></a><a href="#configure-preloading-and-inline-requires" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Configure Preloading and Inline Requires</h3>
<p>Now that we have a RAM bundle, there is overhead for calling <code>require</code>. <code>require</code> now needs to send a message over the bridge when it encounters a module it has not loaded yet. This will impact startup the most, because that is where the largest number of require calls are likely to take place while the app loads the initial module. Luckily we can configure a portion of the modules to be preloaded. In order to do this, you will need to implement some form of inline require.</p>
<h3><a class="anchor" aria-hidden="true" id="investigating-the-loaded-modules"></a><a href="#investigating-the-loaded-modules" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Investigating the Loaded Modules</h3>
<p>In your root file (index.(ios|android).js) you can add the following after the initial imports:</p>
<pre><code class="hljs"><span class="hljs-keyword">const</span> modules = <span class="hljs-built_in">require</span>.getModules();
<span class="hljs-keyword">const</span> moduleIds = <span class="hljs-built_in">Object</span>.keys(modules);
<span class="hljs-keyword">const</span> loadedModuleNames = moduleIds
.filter(<span class="hljs-function"><span class="hljs-params">moduleId</span> =></span> modules[moduleId].isInitialized)
.map(<span class="hljs-function"><span class="hljs-params">moduleId</span> =></span> modules[moduleId].verboseName);
<span class="hljs-keyword">const</span> waitingModuleNames = moduleIds
.filter(<span class="hljs-function"><span class="hljs-params">moduleId</span> =></span> !modules[moduleId].isInitialized)
.map(<span class="hljs-function"><span class="hljs-params">moduleId</span> =></span> modules[moduleId].verboseName);
<span class="hljs-comment">// make sure that the modules you expect to be waiting are actually waiting</span>
<span class="hljs-built_in">console</span>.log(
<span class="hljs-string">'loaded:'</span>,
loadedModuleNames.length,
<span class="hljs-string">'waiting:'</span>,
waitingModuleNames.length
);
<span class="hljs-comment">// grab this text blob, and put it in a file named packager/modulePaths.js</span>
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">`module.exports = <span class="hljs-subst">${<span class="hljs-built_in">JSON</span>.stringify(loadedModuleNames.sort())}</span>;`</span>);
</code></pre>
<p>When you run your app, you can look in the console and see how many modules have been loaded, and how many are waiting. You may want to read the moduleNames and see if there are any surprises. Note that inline requires are invoked the first time the imports are referenced. You may need to investigate and refactor to ensure only the modules you want are loaded on startup. Note that you can change the Systrace object on require to help debug problematic requires.</p>
<pre><code class="hljs"><span class="hljs-built_in">require</span>.Systrace.beginEvent = <span class="hljs-function"><span class="hljs-params">(message)</span> =></span> {
<span class="hljs-keyword">if</span>(message.includes(problematicModule)) {
<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Error();
}
}
</code></pre>
<p>Every app is different, but it may make sense to only load the modules you need for the very first screen. When you are satisfied, put the output of the loadedModuleNames into a file named <code>packager/modulePaths.js</code>.</p>
<h3><a class="anchor" aria-hidden="true" id="updating-the-metroconfigjs"></a><a href="#updating-the-metroconfigjs" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Updating the metro.config.js</h3>
<p>We now need to update <code>metro.config.js</code> in the root of the project to use our newly generated <code>modulePaths.js</code> file:</p>
<pre><code class="hljs"><span class="hljs-keyword">const</span> modulePaths = <span class="hljs-built_in">require</span>(<span class="hljs-string">'./packager/modulePaths'</span>);
<span class="hljs-keyword">const</span> resolve = <span class="hljs-built_in">require</span>(<span class="hljs-string">'path'</span>).resolve;
<span class="hljs-keyword">const</span> fs = <span class="hljs-built_in">require</span>(<span class="hljs-string">'fs'</span>);
<span class="hljs-comment">// Update the following line if the root folder of your app is somewhere else.</span>
<span class="hljs-keyword">const</span> ROOT_FOLDER = resolve(__dirname, <span class="hljs-string">'..'</span>);
<span class="hljs-keyword">const</span> config = {
<span class="hljs-attr">transformer</span>: {
<span class="hljs-attr">getTransformOptions</span>: <span class="hljs-function"><span class="hljs-params">()</span> =></span> {
<span class="hljs-keyword">const</span> moduleMap = {};
modulePaths.forEach(<span class="hljs-function"><span class="hljs-params">path</span> =></span> {
<span class="hljs-keyword">if</span> (fs.existsSync(path)) {
moduleMap[resolve(path)] = <span class="hljs-literal">true</span>;
}
});
<span class="hljs-keyword">return</span> {
<span class="hljs-attr">preloadedModules</span>: moduleMap,
<span class="hljs-attr">transform</span>: { <span class="hljs-attr">inlineRequires</span>: { <span class="hljs-attr">blacklist</span>: moduleMap } },
};
},
},
<span class="hljs-attr">projectRoot</span>:ROOT_FOLDER,
};
<span class="hljs-built_in">module</span>.exports = config;
</code></pre>
<p>The <code>preloadedModules</code> entry in the config indicates which modules should be marked as preloaded when building a RAM bundle. When the bundle is loaded, those modules are immediately loaded, before any requires have even executed. The <code>blacklist</code> entry indicates that those modules should not be required inline. Because they are preloaded, there is no performance benefit from using an inline require. In fact the javascript spends extra time resolving the inline require every time the imports are referenced.</p>
<h3><a class="anchor" aria-hidden="true" id="test-and-measure-improvements"></a><a href="#test-and-measure-improvements" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Test and Measure Improvements</h3>
<p>You should now be ready to build your app using the RAM format and inline requires. Make sure you measure the before and after startup times.</p>
</span></div></article></div><div class="docs-prevnext"><a class="docs-prev button" href="/docs/0.13/debugging"><span class="arrow-prev">← </span><span>Debugging</span></a><a class="docs-next button" href="/docs/0.13/gesture-responder-system"><span>Gesture Responder System</span><span class="arrow-next"> →</span></a></div></div></div><nav class="onPageNav"><ul class="toc-headings"><li><a href="#what-you-need-to-know-about-frames">What you need to know about frames</a><ul class="toc-headings"><li><a href="#js-frame-rate-javascript-thread">JS frame rate (JavaScript thread)</a></li><li><a href="#ui-frame-rate-main-thread">UI frame rate (main thread)</a></li></ul></li><li><a href="#common-sources-of-performance-problems">Common sources of performance problems</a><ul class="toc-headings"><li><a href="#running-in-development-mode-devtrue">Running in development mode (<code>dev=true</code>)</a></li><li><a href="#using-consolelog-statements">Using <code>console.log</code> statements</a></li><li><a href="#listview-initial-rendering-is-too-slow-or-scroll-performance-is-bad-for-large-lists"><code>ListView</code> initial rendering is too slow or scroll performance is bad for large lists</a></li><li><a href="#js-fps-plunges-when-re-rendering-a-view-that-hardly-changes">JS FPS plunges when re-rendering a view that hardly changes</a></li><li><a href="#dropping-js-thread-fps-because-of-doing-a-lot-of-work-on-the-javascript-thread-at-the-same-time">Dropping JS thread FPS because of doing a lot of work on the JavaScript thread at the same time</a></li><li><a href="#moving-a-view-on-the-screen-scrolling-translating-rotating-drops-ui-thread-fps">Moving a view on the screen (scrolling, translating, rotating) drops UI thread FPS</a></li><li><a href="#animating-the-size-of-an-image-drops-ui-thread-fps">Animating the size of an image drops UI thread FPS</a></li><li><a href="#my-touchablex-view-isnt-very-responsive">My TouchableX view isn't very responsive</a></li><li><a href="#slow-navigator-transitions">Slow navigator transitions</a></li></ul></li><li><a href="#profiling">Profiling</a><ul class="toc-headings"><li><a href="#profiling-android-ui-performance-with-systrace">Profiling Android UI Performance with <code>systrace</code></a></li></ul></li><li><a href="#ram-bundles--inline-requires">RAM bundles + inline requires</a><ul class="toc-headings"><li><a href="#loading-javascript">Loading JavaScript</a></li><li><a href="#inline-requires">Inline Requires</a></li><li><a href="#enable-the-ram-format">Enable the RAM format</a></li><li><a href="#configure-preloading-and-inline-requires">Configure Preloading and Inline Requires</a></li><li><a href="#investigating-the-loaded-modules">Investigating the Loaded Modules</a></li><li><a href="#updating-the-metroconfigjs">Updating the metro.config.js</a></li><li><a href="#test-and-measure-improvements">Test and Measure Improvements</a></li></ul></li></ul></nav></div><footer class="nav-footer" id="footer"><section class="sitemap"><div><h5>Docs</h5><a href="/docs/getting-started">Getting Started</a><a href="/docs/tutorial">Tutorial</a><a href="/docs/components-and-apis">Components and APIs</a><a href="/docs/more-resources">More Resources</a></div><div><h5>Community</h5><a href="/help">The React Native Community</a><a href="/showcase">Who's using React Native?</a><a href="https://stackoverflow.com/questions/tagged/react-native" target="_blank">Ask Questions on Stack Overflow</a><a href="https://github.com/facebook/react-native/blob/master/CONTRIBUTING.md">Contributor Guide</a><a href="https://dev.to/t/reactnative" target="_blank">DEV Community</a></div><div><h5>More Resources</h5><a href="/blog">Blog</a><a href="https://twitter.com/reactnative" target="_blank">Twitter</a><a href="https://github.com/facebook/react-native" target="_blank">GitHub</a><a href="https://reactjs.org" target="_blank">React</a></div></section><a href="https://code.facebook.com/projects/" target="_blank" class="fbOpenSource"><img src="/img/oss_logo.png" alt="Facebook Open Source" width="170" height="45"/></a><section class="copyright">Copyright © 2020 Facebook Inc.</section></footer></div><script type="text/javascript" src="https://cdn.jsdelivr.net/docsearch.js/1/docsearch.min.js"></script><script>window.fbAsyncInit = function() {FB.init({appId:'1677033832619985',xfbml:true,version:'v2.7'});};(function(d, s, id){var js, fjs = d.getElementsByTagName(s)[0];if (d.getElementById(id)) {return;}js = d.createElement(s); js.id = id;js.src = '//connect.facebook.net/en_US/sdk.js';fjs.parentNode.insertBefore(js, fjs);}(document, 'script','facebook-jssdk'));
</script><script>window.twttr=(function(d,s, id){var js,fjs=d.getElementsByTagName(s)[0],t=window.twttr||{};if(d.getElementById(id))return t;js=d.createElement(s);js.id=id;js.src='https://platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js, fjs);t._e = [];t.ready = function(f) {t._e.push(f);};return t;}(document, 'script', 'twitter-wjs'));</script><script>
document.addEventListener('keyup', function(e) {
if (e.target !== document.body) {
return;
}
// keyCode for '/' (slash)
if (e.keyCode === 191) {
const search = document.getElementById('search_input_react');
search && search.focus();
}
});
</script><script>
var search = docsearch({
apiKey: '2c98749b4a1e588efec53b2acec13025',
indexName: 'react-native-versions',
inputSelector: '#search_input_react',
algoliaOptions: {"facetFilters":["tags:0.13"],"hitsPerPage":5}
});
</script></body></html>