SlideShare a Scribd company logo
1 of 46
<KyivTalk
title="React-Native Rendering Performance"
author="Ilya Ivanov"
/>
About Me
• React-Native 3 years
• React.js 5 years
• Software Development 8 years
Ilya Ivanov
Why this talk
Performance
Functionality
Developer
Experience
is challenging
Plan
• Why we should care about render counts
• Where to focus our effort
• How to avoid redundant renders
What we are
going to build
Non-Optimized
Non-Optimized
Press – 1.72s
Changes – 2.24s
Response – 520ms
QuickTime Player on MacOS
Shortcut ⌘ + T
Non-Optimized
Non-Optimized
Press – 2.34s
Changes – 4.18s
Response – 1840ms
Non-Optimized Results
• 100 Artists – 520ms
• 300 Artists – 1840ms
• Golden rule – 200ms
Performance
Degradation
Slow performance stacks
Better
Performance
Summary
• Performance is already slow for small sample 100 artists (500ms)
• Performance degrades with more data
• Slow responsiveness stack because of JS single threaded
Where to focus our effort
• 1 to * relationships in our hierarchy
• <FlatList or {items.map(item => …}
• No control over the number of items in our hierarchy
Page
Artist 1
StatusBar
Box 1 Box 2... Box 8
Artist 2 …
StatusBar2
Artist N
StatusBarN
Update Complexity (big O for onPress)
• Current – O(n * m)
• n – number of artists
• m – number of boxes
• Goal – O(1)
Page
Artist 1
StatusBar
Box 1 Box 2... Box 8
Artist 2 …
StatusBar2
Artist N
StatusBarN
How to avoid redundant renders
• shouldComponentUpdate
• PureComponent
• React.memo
shouldComponentUpdate
render?
propsstate
shouldComponentUpdate(nextProps, nextState, nextContext) {
return this.props.id !== nextProps.id;
}
context
Functionality
PureComponent
Used for classes. Shallow compare === for all properties of props, state
and context
React.memo
Used for function. Shallow compare === for all properties of props and
context
ArtistInfo component
renderArtist(artist) {
const artistStyle = [
styles.artistContainer,
artist.isSelected && styles.selectedArtist
];
return (
<ArtistInfo
style={artistStyle}
onPress={() => this.onPress(artist.id)}
fontStyle={{ fontSize: 23 }}
popularity={artist.popularity} //0..1
/>
);
}
What does it mean to break PureComponent
const ArtistInfo = React.memo(/.../);
<ArtistInfo
myParameter={{property: 42}}
/>
{} === {} //false
{property: 1} === {property: 1} //false
[] === [] //false
(() => undefined) === (() => undefined) //false
'1' === '1' //true
1 === 1 //true
Fixing onPress
onPress(id) {
this.setState(...)
}
renderArtist(artist) {
return (
<ArtistInfo
onPress={() => this.onPress(artist.id)}
/>
);
}
class ArtistInfo extends PureComponent {
render() {
//...
<TouchableOpacity onPress={this.props.onPress}
onPress(id) {
this.setState(...)
}
renderArtist(artist) {
return (
<ArtistInfo
id={artist.id}
onPress={this.onPress}
/>
);
}
class ArtistInfo extends PureComponent {
render() {
//...
<TouchableOpacity
onPress={() => this.props.onPress(this.props.id)}
onPress(id) {
this.setState(...)
// ERROR: setState is not a function
}
renderArtist(artist) {
return (
<ArtistInfo
id={artist.id}
onPress={this.onPress}
/>
);
}
onPress = (id) => {
// works fine...
// Breaks Hot Reloading
}
renderArtist(artist) {
return (
<ArtistInfo
id={artist.id}
onPress={this.onPress}
/>
);
}
https://github.com/facebook/react-native/issues/10991
Developer
Experience
Fast refresh replaces hot refresh and live
reloading
onPress = (id) => {
// works fine since 0.61
}
renderArtist(artist) {
return (
<ArtistInfo
id={artist.id}
onPress={this.onPress}
/>
);
}
Fixing styles
renderArtist(artist) {
const artistStyle = [
styles.artistContainer,
artist.isSelected && styles.selectedArtist
];
return (
<ArtistInfo
style={artistStyle}
fontStyle={{ fontSize: 23 }}
/>
);
}
const styles = StyleSheet.create({
artistContainer: {
//common styles
},
selectedArtist: {
//specific styles
}
});
Fixing styles const styles = StyleSheet.create({
artistContainer: {
//common styles
},
selectedArtist: {
//specific styles
},
artistFont:{
fontSize: 23
}
});
renderArtist(artist) {
const artistStyle = [
styles.artistContainer,
artist.isSelected && styles.selectedArtist
];
return (
<ArtistInfo
style={artistStyle}
fontStyle={styles.artistFont}
/>
);
}
Fixing styles const styles = StyleSheet.create({
artistContainer: {
//common styles
},
selectedArtist: {
//specific styles
},
artistFont:{
fontSize: 23
}
});
selectedArtistStyle = [
styles.artistContainer,
styles.selectedArtist,
];
renderArtist(artist) {
const artistStyle = artist.isSelected
? this.selectedArtistStyle
: styles.artistContainer;
return (
<ArtistInfo
style={artistStyle}
fontStyle={styles.artistFont}
/>
);
}
Common render troublemakers
• {}
• []
• () => this.handle(someData)
Optimized
Results
Careless O(n * m)
100 Artists – 520ms
300 Artists – 1840ms
Optimized O(1)
100 Artists – 130ms
300 Artists – 150ms
Fixing popularity
renderArtist(artist) {
return (
<ArtistInfo
popularity={artist.popularity} //0..1
/>
);
}
• Artist A 3 000 000 – 1
• Artist B 20 000 – 0.00666666667
• Artist C 1 020 000 – 0.34
• Artist D 3 000 001 – 1
popularity = artist.listeners / maxListeners;
3 000 0001 020 0000.34
• Artist A 3 000 000 – 1 => 0.999999667
• Artist B 20 000 – 0.00666666667 => 0.00666666444
• Artist C 1 020 000 – 0.34 => 0.339999887
• Artist D 3 000 001 – 1
popularity = artist.listeners / maxListeners;
3 000 0001 020 0000.34
• Artist A 3 000 000 – 100
• Artist B 20 000 – 0
• Artist C 1 020 000 – 34
• Artist D 3 000 001 – 100
popularity = Math.round(artist.listeners / maxListeners * 100);
1 020 000 3 000 00034
• Artist A 3 000 000 – 100 => 100
• Artist B 20 000 – 0 => 0
• Artist C 1 020 000 – 34 => 34
• Artist D 3 000 001 – 100
popularity = Math.round(artist.listeners / maxListeners * 100);
1 020 000 3 000 00034
Tools
• ESLint rules
• why-did-you-update
• why-did-you-render
• React profiles
• console.log
• Sandboxed pages (aka Storybook without Storybook)
React Hooks
const MyFunctionalComponent = () => {
const [timesPressed, setTimesPressed] = useState(0);
const increment = () => setTimesPressed(timesPressed + 1);
return (
<View>
<ArtistInfo onPress={increment} />
<Text>{timesPressed}</Text>
</View>
);
};
Performance
Functionality
Developer
Experience
const useCounter = () => {
const [counter, setCounter] = useState(0);
const increment = () => setCounter(counter + 1);
return [counter, increment];
};
const MyFunctionalComponent = () => {
const [timesPressed, setTimesPressed] = useCounter();
//...
Developer
Experience
Performance
Functionality
const useCounter = () => {
const [counter, setCounter] = useState(0);
const increment = useCallback(
() => setCounter(counter + 1),
[]
);
return [counter, increment];
};
const useCounter = () => {
const [counter, setCounter] = useState(0);
const increment = useCallback(
() => setCounter(counter + 1),
[counter]
);
return [counter, increment];
};
<View>
<ArtistInfo onPress={increment} />
<Text>{timesPressed}</Text>
</View>
const [timesPressed, increment] = useCounter();
const reducer = state => state + 1;
const useCounter = () => {
const [counter, increment] = useReducer(reducer, 0);
return [counter, increment];
};
const reducer = state => state + 1;
const useCounter = () => useReducer(reducer, 0);
<View>
<ArtistInfo onPress={increment} />
<Text>{timesPressed}</Text>
</View>
Performance
Functionality
Developer
Experience
const [timesPressed, increment] = useCounter();const useCounter = () => {
const [counter, setCounter] = useState(0);
const increment = useCallback(
() => setCounter(counter + 1),
[counter]
);
return [counter, increment];
};
import {useState, useCallback, useReducer} from 'react';
Summary
• React-Native doesn’t have to have performance problems
• Focus on measurement first, then optimize
• Prioritize potentially limitless content
• When optimizing don’t overlook developer experience and
functionality
Thanks
Slides: http://bit.ly/WixPerformance2019
Repo: https://github.com/ilyaivanov/ReactNativePerformance

More Related Content

Similar to React-Native Rendering Performance

JS Lab2017_Lightning Talks_React Perfomance
JS Lab2017_Lightning Talks_React Perfomance JS Lab2017_Lightning Talks_React Perfomance
JS Lab2017_Lightning Talks_React Perfomance GeeksLab Odessa
 
React Performance
React PerformanceReact Performance
React PerformanceMax Kudla
 
A Tour of Building Web Applications with R Shiny
A Tour of Building Web Applications with R Shiny A Tour of Building Web Applications with R Shiny
A Tour of Building Web Applications with R Shiny Wendy Chen Dubois
 
Redux Sagas - React Alicante
Redux Sagas - React AlicanteRedux Sagas - React Alicante
Redux Sagas - React AlicanteIgnacio Martín
 
Redux saga: managing your side effects. Also: generators in es6
Redux saga: managing your side effects. Also: generators in es6Redux saga: managing your side effects. Also: generators in es6
Redux saga: managing your side effects. Also: generators in es6Ignacio Martín
 
Integrating React.js with PHP projects
Integrating React.js with PHP projectsIntegrating React.js with PHP projects
Integrating React.js with PHP projectsIgnacio Martín
 
Wrap up Presentation Flux/Redux
Wrap up Presentation Flux/ReduxWrap up Presentation Flux/Redux
Wrap up Presentation Flux/ReduxSylvain Faucherand
 
Full Stack Toronto - the 3R Stack
Full Stack Toronto - the 3R StackFull Stack Toronto - the 3R Stack
Full Stack Toronto - the 3R StackScott Persinger
 
Adopting F# at SBTech
Adopting F# at SBTechAdopting F# at SBTech
Adopting F# at SBTechAntya Dev
 
React.js workshop by tech47.in
React.js workshop by tech47.inReact.js workshop by tech47.in
React.js workshop by tech47.inJaikant Kumaran
 
The journey of an (un)orthodox optimization
The journey of an (un)orthodox optimizationThe journey of an (un)orthodox optimization
The journey of an (un)orthodox optimizationSian Lerk Lau
 
Dive into React Performance
Dive into React PerformanceDive into React Performance
Dive into React PerformanceChing Ting Wu
 
React.js Basics - ConvergeSE 2015
React.js Basics - ConvergeSE 2015React.js Basics - ConvergeSE 2015
React.js Basics - ConvergeSE 2015Robert Pearce
 
The Challenge of Bringing FEZ to PlayStation Platforms
The Challenge of Bringing FEZ to PlayStation PlatformsThe Challenge of Bringing FEZ to PlayStation Platforms
The Challenge of Bringing FEZ to PlayStation PlatformsMiguel Angel Horna
 
Structuring React.js Components
Structuring React.js ComponentsStructuring React.js Components
Structuring React.js ComponentsBartek Witczak
 
4Developers 2018: Structuring React components (Bartłomiej Witczak)
4Developers 2018: Structuring React components (Bartłomiej Witczak)4Developers 2018: Structuring React components (Bartłomiej Witczak)
4Developers 2018: Structuring React components (Bartłomiej Witczak)PROIDEA
 
F# in social gaming by Yan Cui at Codemotion Dubai
F# in social gaming by Yan Cui at Codemotion DubaiF# in social gaming by Yan Cui at Codemotion Dubai
F# in social gaming by Yan Cui at Codemotion DubaiCodemotion Dubai
 
F# in the real world (CodeMotion Dubai)
F# in the real world (CodeMotion Dubai)F# in the real world (CodeMotion Dubai)
F# in the real world (CodeMotion Dubai)Yan Cui
 

Similar to React-Native Rendering Performance (20)

JS Lab2017_Lightning Talks_React Perfomance
JS Lab2017_Lightning Talks_React Perfomance JS Lab2017_Lightning Talks_React Perfomance
JS Lab2017_Lightning Talks_React Perfomance
 
React Performance
React PerformanceReact Performance
React Performance
 
A Tour of Building Web Applications with R Shiny
A Tour of Building Web Applications with R Shiny A Tour of Building Web Applications with R Shiny
A Tour of Building Web Applications with R Shiny
 
Redux Sagas - React Alicante
Redux Sagas - React AlicanteRedux Sagas - React Alicante
Redux Sagas - React Alicante
 
Redux saga: managing your side effects. Also: generators in es6
Redux saga: managing your side effects. Also: generators in es6Redux saga: managing your side effects. Also: generators in es6
Redux saga: managing your side effects. Also: generators in es6
 
Integrating React.js with PHP projects
Integrating React.js with PHP projectsIntegrating React.js with PHP projects
Integrating React.js with PHP projects
 
Wrap up Presentation Flux/Redux
Wrap up Presentation Flux/ReduxWrap up Presentation Flux/Redux
Wrap up Presentation Flux/Redux
 
Full Stack Toronto - the 3R Stack
Full Stack Toronto - the 3R StackFull Stack Toronto - the 3R Stack
Full Stack Toronto - the 3R Stack
 
Adopting F# at SBTech
Adopting F# at SBTechAdopting F# at SBTech
Adopting F# at SBTech
 
React.js workshop by tech47.in
React.js workshop by tech47.inReact.js workshop by tech47.in
React.js workshop by tech47.in
 
The journey of an (un)orthodox optimization
The journey of an (un)orthodox optimizationThe journey of an (un)orthodox optimization
The journey of an (un)orthodox optimization
 
App bot
App botApp bot
App bot
 
Dive into React Performance
Dive into React PerformanceDive into React Performance
Dive into React Performance
 
React.js Basics - ConvergeSE 2015
React.js Basics - ConvergeSE 2015React.js Basics - ConvergeSE 2015
React.js Basics - ConvergeSE 2015
 
The Challenge of Bringing FEZ to PlayStation Platforms
The Challenge of Bringing FEZ to PlayStation PlatformsThe Challenge of Bringing FEZ to PlayStation Platforms
The Challenge of Bringing FEZ to PlayStation Platforms
 
SOLID Ruby, SOLID Rails
SOLID Ruby, SOLID RailsSOLID Ruby, SOLID Rails
SOLID Ruby, SOLID Rails
 
Structuring React.js Components
Structuring React.js ComponentsStructuring React.js Components
Structuring React.js Components
 
4Developers 2018: Structuring React components (Bartłomiej Witczak)
4Developers 2018: Structuring React components (Bartłomiej Witczak)4Developers 2018: Structuring React components (Bartłomiej Witczak)
4Developers 2018: Structuring React components (Bartłomiej Witczak)
 
F# in social gaming by Yan Cui at Codemotion Dubai
F# in social gaming by Yan Cui at Codemotion DubaiF# in social gaming by Yan Cui at Codemotion Dubai
F# in social gaming by Yan Cui at Codemotion Dubai
 
F# in the real world (CodeMotion Dubai)
F# in the real world (CodeMotion Dubai)F# in the real world (CodeMotion Dubai)
F# in the real world (CodeMotion Dubai)
 

React-Native Rendering Performance

  • 2. About Me • React-Native 3 years • React.js 5 years • Software Development 8 years Ilya Ivanov
  • 4. Plan • Why we should care about render counts • Where to focus our effort • How to avoid redundant renders
  • 5. What we are going to build
  • 7. Non-Optimized Press – 1.72s Changes – 2.24s Response – 520ms QuickTime Player on MacOS Shortcut ⌘ + T
  • 9. Non-Optimized Press – 2.34s Changes – 4.18s Response – 1840ms
  • 10. Non-Optimized Results • 100 Artists – 520ms • 300 Artists – 1840ms • Golden rule – 200ms
  • 13. Summary • Performance is already slow for small sample 100 artists (500ms) • Performance degrades with more data • Slow responsiveness stack because of JS single threaded
  • 14. Where to focus our effort • 1 to * relationships in our hierarchy • <FlatList or {items.map(item => …} • No control over the number of items in our hierarchy
  • 15. Page Artist 1 StatusBar Box 1 Box 2... Box 8 Artist 2 … StatusBar2 Artist N StatusBarN
  • 16. Update Complexity (big O for onPress) • Current – O(n * m) • n – number of artists • m – number of boxes • Goal – O(1) Page Artist 1 StatusBar Box 1 Box 2... Box 8 Artist 2 … StatusBar2 Artist N StatusBarN
  • 17. How to avoid redundant renders • shouldComponentUpdate • PureComponent • React.memo
  • 18. shouldComponentUpdate render? propsstate shouldComponentUpdate(nextProps, nextState, nextContext) { return this.props.id !== nextProps.id; } context Functionality
  • 19. PureComponent Used for classes. Shallow compare === for all properties of props, state and context React.memo Used for function. Shallow compare === for all properties of props and context
  • 20. ArtistInfo component renderArtist(artist) { const artistStyle = [ styles.artistContainer, artist.isSelected && styles.selectedArtist ]; return ( <ArtistInfo style={artistStyle} onPress={() => this.onPress(artist.id)} fontStyle={{ fontSize: 23 }} popularity={artist.popularity} //0..1 /> ); }
  • 21. What does it mean to break PureComponent const ArtistInfo = React.memo(/.../); <ArtistInfo myParameter={{property: 42}} /> {} === {} //false {property: 1} === {property: 1} //false [] === [] //false (() => undefined) === (() => undefined) //false '1' === '1' //true 1 === 1 //true
  • 22. Fixing onPress onPress(id) { this.setState(...) } renderArtist(artist) { return ( <ArtistInfo onPress={() => this.onPress(artist.id)} /> ); } class ArtistInfo extends PureComponent { render() { //... <TouchableOpacity onPress={this.props.onPress}
  • 23. onPress(id) { this.setState(...) } renderArtist(artist) { return ( <ArtistInfo id={artist.id} onPress={this.onPress} /> ); } class ArtistInfo extends PureComponent { render() { //... <TouchableOpacity onPress={() => this.props.onPress(this.props.id)}
  • 24. onPress(id) { this.setState(...) // ERROR: setState is not a function } renderArtist(artist) { return ( <ArtistInfo id={artist.id} onPress={this.onPress} /> ); }
  • 25. onPress = (id) => { // works fine... // Breaks Hot Reloading } renderArtist(artist) { return ( <ArtistInfo id={artist.id} onPress={this.onPress} /> ); } https://github.com/facebook/react-native/issues/10991 Developer Experience
  • 26. Fast refresh replaces hot refresh and live reloading onPress = (id) => { // works fine since 0.61 } renderArtist(artist) { return ( <ArtistInfo id={artist.id} onPress={this.onPress} /> ); }
  • 27. Fixing styles renderArtist(artist) { const artistStyle = [ styles.artistContainer, artist.isSelected && styles.selectedArtist ]; return ( <ArtistInfo style={artistStyle} fontStyle={{ fontSize: 23 }} /> ); } const styles = StyleSheet.create({ artistContainer: { //common styles }, selectedArtist: { //specific styles } });
  • 28. Fixing styles const styles = StyleSheet.create({ artistContainer: { //common styles }, selectedArtist: { //specific styles }, artistFont:{ fontSize: 23 } }); renderArtist(artist) { const artistStyle = [ styles.artistContainer, artist.isSelected && styles.selectedArtist ]; return ( <ArtistInfo style={artistStyle} fontStyle={styles.artistFont} /> ); }
  • 29. Fixing styles const styles = StyleSheet.create({ artistContainer: { //common styles }, selectedArtist: { //specific styles }, artistFont:{ fontSize: 23 } }); selectedArtistStyle = [ styles.artistContainer, styles.selectedArtist, ]; renderArtist(artist) { const artistStyle = artist.isSelected ? this.selectedArtistStyle : styles.artistContainer; return ( <ArtistInfo style={artistStyle} fontStyle={styles.artistFont} /> ); }
  • 30. Common render troublemakers • {} • [] • () => this.handle(someData)
  • 32. Results Careless O(n * m) 100 Artists – 520ms 300 Artists – 1840ms Optimized O(1) 100 Artists – 130ms 300 Artists – 150ms
  • 33. Fixing popularity renderArtist(artist) { return ( <ArtistInfo popularity={artist.popularity} //0..1 /> ); }
  • 34. • Artist A 3 000 000 – 1 • Artist B 20 000 – 0.00666666667 • Artist C 1 020 000 – 0.34 • Artist D 3 000 001 – 1 popularity = artist.listeners / maxListeners; 3 000 0001 020 0000.34
  • 35. • Artist A 3 000 000 – 1 => 0.999999667 • Artist B 20 000 – 0.00666666667 => 0.00666666444 • Artist C 1 020 000 – 0.34 => 0.339999887 • Artist D 3 000 001 – 1 popularity = artist.listeners / maxListeners; 3 000 0001 020 0000.34
  • 36. • Artist A 3 000 000 – 100 • Artist B 20 000 – 0 • Artist C 1 020 000 – 34 • Artist D 3 000 001 – 100 popularity = Math.round(artist.listeners / maxListeners * 100); 1 020 000 3 000 00034
  • 37. • Artist A 3 000 000 – 100 => 100 • Artist B 20 000 – 0 => 0 • Artist C 1 020 000 – 34 => 34 • Artist D 3 000 001 – 100 popularity = Math.round(artist.listeners / maxListeners * 100); 1 020 000 3 000 00034
  • 38. Tools • ESLint rules • why-did-you-update • why-did-you-render • React profiles • console.log • Sandboxed pages (aka Storybook without Storybook)
  • 39. React Hooks const MyFunctionalComponent = () => { const [timesPressed, setTimesPressed] = useState(0); const increment = () => setTimesPressed(timesPressed + 1); return ( <View> <ArtistInfo onPress={increment} /> <Text>{timesPressed}</Text> </View> ); }; Performance Functionality Developer Experience
  • 40. const useCounter = () => { const [counter, setCounter] = useState(0); const increment = () => setCounter(counter + 1); return [counter, increment]; }; const MyFunctionalComponent = () => { const [timesPressed, setTimesPressed] = useCounter(); //... Developer Experience Performance
  • 41. Functionality const useCounter = () => { const [counter, setCounter] = useState(0); const increment = useCallback( () => setCounter(counter + 1), [] ); return [counter, increment]; };
  • 42. const useCounter = () => { const [counter, setCounter] = useState(0); const increment = useCallback( () => setCounter(counter + 1), [counter] ); return [counter, increment]; }; <View> <ArtistInfo onPress={increment} /> <Text>{timesPressed}</Text> </View> const [timesPressed, increment] = useCounter();
  • 43. const reducer = state => state + 1; const useCounter = () => { const [counter, increment] = useReducer(reducer, 0); return [counter, increment]; };
  • 44. const reducer = state => state + 1; const useCounter = () => useReducer(reducer, 0); <View> <ArtistInfo onPress={increment} /> <Text>{timesPressed}</Text> </View> Performance Functionality Developer Experience const [timesPressed, increment] = useCounter();const useCounter = () => { const [counter, setCounter] = useState(0); const increment = useCallback( () => setCounter(counter + 1), [counter] ); return [counter, increment]; }; import {useState, useCallback, useReducer} from 'react';
  • 45. Summary • React-Native doesn’t have to have performance problems • Focus on measurement first, then optimize • Prioritize potentially limitless content • When optimizing don’t overlook developer experience and functionality