Slide 1

Slide 1 text

@stevekinney Demystifying Performance in React Steve Kinney NationJS React: December, 2018

Slide 2

Slide 2 text

@stevekinney Hi, I’m Steve. (@stevekinney)

Slide 3

Slide 3 text

@stevekinney

Slide 4

Slide 4 text

@stevekinney

Slide 5

Slide 5 text

@stevekinney We’re going to think about performance.

Slide 6

Slide 6 text

@stevekinney Chapter One What is performance and why does it matter?

Slide 7

Slide 7 text

@stevekinney Why does performance matter?

Slide 8

Slide 8 text

@stevekinney Allow me to use some slides with too many words and then read them too you. #thoughtleadership101

Slide 9

Slide 9 text

@stevekinney Actually, I’m not…

Slide 10

Slide 10 text

“ @stevekinney 0.1 second is about the limit for having the user feel that the system is reacting instantaneously, meaning that no special feedback is necessary except to display the result. —Jakob Nielsen

Slide 11

Slide 11 text

“ @stevekinney 1.0 second is about the limit for the user's flow of thought to stay uninterrupted, even though the user will notice the delay. Normally, no special feedback is necessary during delays of more than 0.1 but less than 1.0 second, but the user does lose the feeling of operating directly on the data. —Jakob Nielsen

Slide 12

Slide 12 text

“ @stevekinney 10 seconds is about the limit for keeping the user's attention focused on the dialogue. For longer delays, users will want to perform other tasks while waiting for the computer to finish, so they should be given feedback indicating when the computer expects to be done. Feedback during the delay is especially important if the response time is likely to be highly variable, since users will then not know what to expect. —Jakob Nielsen

Slide 13

Slide 13 text

@stevekinney

Slide 14

Slide 14 text

@stevekinney Now I’ll show you some statistics to make myself sound smart.

Slide 15

Slide 15 text

@stevekinney Aberdeen Group found that a 1 second slow down resulted 11% fewer page views, 7% less conversion. Source.

Slide 16

Slide 16 text

@stevekinney Akamai found that two-second delay in web page load time increase bounce rates by 103 percent. Source.

Slide 17

Slide 17 text

@stevekinney A 400 millisecond improvement in performance resulted in a 9% increase in traffic at Yahoo. Source.

Slide 18

Slide 18 text

@stevekinney Google found that a 2% slower page resulted in 2% fewer searches, which means 2% fewer ads shown. Source.

Slide 19

Slide 19 text

@stevekinney 100 millisecond improvement in performance results in 1% increase in overall revenue at Amazon. Source.

Slide 20

Slide 20 text

@stevekinney One more thing…

Slide 21

Slide 21 text

@stevekinney According to “research” , if you want user to feel like your site is faster than your competitors, you need to be 20% faster.

Slide 22

Slide 22 text

@stevekinney Thinking About Performance

Slide 23

Slide 23 text

@stevekinney

Slide 24

Slide 24 text

@stevekinney

Slide 25

Slide 25 text

@stevekinney There are different kinds of performance.

Slide 26

Slide 26 text

@stevekinney

Slide 27

Slide 27 text

@stevekinney

Slide 28

Slide 28 text

“ @stevekinney Measure. Don’t tune for speed until you’ve measured, and even then don’t unless one part of the code overwhelms the rest. —Rob Pike

Slide 29

Slide 29 text

@stevekinney What matters to you? • The New York Times might care about time to first headline. • Twitter might care about time to first tweet. • Chrome might care about time to inspect element. • What does your product or project care about?

Slide 30

Slide 30 text

@stevekinney Don’t get carried away with measuring, either.

Slide 31

Slide 31 text

@stevekinney Thinking deeply about the architecture of your application is a better use of your time than micro-benchmarks.

Slide 32

Slide 32 text

@stevekinney Three Tiers of Advice • Definitely do this. • Maybe do this, but measure before and after. • Only do this if you find a performance problem that needs solving.

Slide 33

Slide 33 text

@stevekinney What are you sacrificing? Readability? Reliability?

Slide 34

Slide 34 text

@stevekinney Some Disclaimers • React is decently fast out of the box. • Being all crazy about performance at the expense of creating value for your users is not awesome. • We’re going to look at some contrived examples because most larger code bases are: private, hard to grok during at talk, and hard to fake.

Slide 35

Slide 35 text

@stevekinney And now: Steve’s Golden Rule of Performance

Slide 36

Slide 36 text

@stevekinney Doing Less Stuff Takes Less Time.

Slide 37

Slide 37 text

@stevekinney We’ll cover some of these concepts today. • Don’t reconcile the virtual DOM if you don’t have do. • Keep your state flat and primitive. • Don’t add functions to the call stack if you don’t need to. • Share things that don’t change instead of reinventing the wheel.

Slide 38

Slide 38 text

@stevekinney Chapter Two Who Hurt You?

Slide 39

Slide 39 text

@stevekinney React 16 lets your measure components’ lifecycles using the User Timing API.

Slide 40

Slide 40 text

@stevekinney Three Main Requirements • React 16+ in development mode. • Source maps. • Chrome developer tools.

Slide 41

Slide 41 text

@stevekinney ?react_perf

Slide 42

Slide 42 text

@stevekinney

Slide 43

Slide 43 text

@stevekinney

Slide 44

Slide 44 text

@stevekinney

Slide 45

Slide 45 text

@stevekinney Chapter Three Optimizing React Performance

Slide 46

Slide 46 text

@stevekinney Free tip: Make sure you’re running in Production Mode.

Slide 47

Slide 47 text

@stevekinney What is production mode? • PropTypes? Nah. • “Helpful” warnings? No, thanks. • Performance metrics? Nope. • Not having this stuff relieves React of a bunch of work and allows it to run fast.

Slide 48

Slide 48 text

@stevekinney

Slide 49

Slide 49 text

@stevekinney

Slide 50

Slide 50 text

@stevekinney new webpack.DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify('production') }), new webpack.optimize.UglifyJsPlugin()

Slide 51

Slide 51 text

@stevekinney Question: What makes React fast?

Slide 52

Slide 52 text

@stevekinney Answer: The virtual DOM™. • Making changes to the real DOM is slow. • Making lots of little changes to the DOM can add up. • The virtual DOM let’s us change an object in memory—which is way faster. • Then it figures out of to do the most efficient change all at once.

Slide 53

Slide 53 text

@stevekinney React’s reconciliation process compares the previous output from the render() method with the new one.

Slide 54

Slide 54 text

@stevekinney Corollary: Not checking the component tree is faster than checking it.

Slide 55

Slide 55 text

@stevekinney shouldComponentUpdate()

Slide 56

Slide 56 text

@stevekinney

Slide 57

Slide 57 text

@stevekinney class MyComponent extends React.Component { shouldComponentUpdate() { return true; } }

Slide 58

Slide 58 text

@stevekinney class MyComponent extends React.Component { shouldComponentUpdate() { return false; } }

Slide 59

Slide 59 text

@stevekinney class UserAvatar extends React.Component { shouldComponentUpdate(nextProps, nextState) { return nextProps.user.avatar === this.props.user.avatar; } }

Slide 60

Slide 60 text

@stevekinney

Slide 61

Slide 61 text

@stevekinney Pure Components

Slide 62

Slide 62 text

@stevekinney class MyComponent extends React.Component { shouldComponentUpdate(nextProps, nextState) { return ( shallowCompare(this.props, nextProps) && shallowCompare(this.state, nextState); ); } }

Slide 63

Slide 63 text

@stevekinney const FunctionalPureComponent = React.memo((props) => { /* only rerenders if props change */ });

Slide 64

Slide 64 text

@stevekinney A Word of Warning

Slide 65

Slide 65 text

@stevekinney

Slide 66

Slide 66 text

@stevekinney

Slide 67

Slide 67 text

@stevekinney Moving Stuff Around (Or, “Not Being Lazy with Keys”)

Slide 68

Slide 68 text

@stevekinney That annoying warning about omitting a key when iterating over a collection is actually pretty important…

Slide 69

Slide 69 text

@stevekinney It helps React figure out whether this is something new or just an existing thing that’s moved.

Slide 70

Slide 70 text

@stevekinney const Posts = (posts) => ( { posts.map((post, index) => ) } ); const Posts = (posts) => ( { posts.map((post) => ) } );

Slide 71

Slide 71 text

@stevekinney Action Items • Use a unique identifier as the key. • Don’t use the index of the item in the array. • FFS, don’t use Math.random(). You are better than that.

Slide 72

Slide 72 text

@stevekinney Chapter Four State Management

Slide 73

Slide 73 text

@stevekinney How you manage state in your application can have some important implications on the performance of your application.

Slide 74

Slide 74 text

@stevekinney Prefer Primitives

Slide 75

Slide 75 text

@stevekinney Primitives (strings, numbers, booleans) are passed by value.

Slide 76

Slide 76 text

@stevekinney Objects—including arrays and functions—are passed by reference.

Slide 77

Slide 77 text

@stevekinney 1 === 1; 'a' === 'a'; true === true; { a: 1 } !== { a: 1 } [1, 2, 3] !== [1, 2, 3] (a, b) => a + b !== (a, b) => a + b;

Slide 78

Slide 78 text

@stevekinney Keep Your Data Flat

Slide 79

Slide 79 text

@stevekinney const posts = { first: { title: 'The Good News About React Hooks', description: 'They exist, but not yet.', }, second: { title: 'Classes Are Dead! Long Live Classes!', description: "Or, maybe not. I don't really know.", }, }; posts.second = { ...posts.second, description: 'Definitely not soon.' };

Slide 80

Slide 80 text

@stevekinney posts === posts;

Slide 81

Slide 81 text

@stevekinney const posts = { first: { title: 'The Good News About React Hooks', description: 'They exist, but not yet.', comments: { 123: { author: 'Steve Kinney', bio: { twitter: '@stevekinney', company: 'SendGrid', }, body: 'First!', responses: { 456: { author: 'Ksenia Coulter', body: 'You never sent me your fun fact!', // What if I want to update this? } }, }, }, }, // … };

Slide 82

Slide 82 text

@stevekinney this.setState({ posts: { ...this.state.posts, first: { ...this.state.posts.first, comments: { ...this.state.posts.first.comments, responses: { ...this.state.posts.first.comments.responses, firstResponse: { ...this.state.posts.first.comments.firstResponse, body: 'What is your spirit animal?' } } } } } });

Slide 83

Slide 83 text

@stevekinney const state = { posts: { firstPostId: { title: 'The Good News About React Hooks', description: 'They exist, but not yet.', author: 'firstAuthorId', comments: ['firstCommentId', 'secondCommentId'], }, // … }, authors: { firstAuthorId: { name: 'Robbie Holmes', twitter: ‘@robbiethegeek', }, }, comments: { firstCommentId: { body: 'Commenting on my own post!', author: 'firstAuthorId', }, // … }, };

Slide 84

Slide 84 text

@stevekinney paularmstrong/normalizr

Slide 85

Slide 85 text

@stevekinney { "id": "123", "author": { "id": "1", "name": "Paul" }, "title": "My awesome blog post", "comments": [ { "id": "324", "commenter": { "id": "2", "name": "Nicole" } } ] }

Slide 86

Slide 86 text

@stevekinney import { normalize, schema } from 'normalizr'; const user = new schema.Entity('users'); const comment = new schema.Entity('comments', { commenter: user }); const article = new schema.Entity('articles', { author: user, comments: [comment] }); const normalizedData = normalize(originalData, article);

Slide 87

Slide 87 text

@stevekinney { result: "123", entities: { "articles": { "123": { id: "123", author: "1", title: "My awesome blog post", comments: [ "324" ] } }, "users": { "1": { "id": "1", "name": "Paul" }, "2": { "id": "2", "name": "Nicole" } }, "comments": { "324": { id: "324", "commenter": "2" } } } }

Slide 88

Slide 88 text

@stevekinney You wouldn’t be wrong to think of your application state as an in- memory, NoSQL database.

Slide 89

Slide 89 text

@stevekinney Pro Tip: Break up your containers.

Slide 90

Slide 90 text

@stevekinney Me, like a year or two ago… const PostsList = posts => ( {posts.map(post => ( ))} ); const PostsListContainer = connect(posts => posts);

Slide 91

Slide 91 text

@stevekinney Me, today… const PostsList = (ids) => ( {ids.map(id => ( ))} );

Slide 92

Slide 92 text

@stevekinney Memoize Your Containers

Slide 93

Slide 93 text

@stevekinney Reselect

Slide 94

Slide 94 text

@stevekinney Fun Fact About Selectors • Selectors can compute derived data, allowing Redux to store the minimal possible state. • Selectors are efficient. A selector is not recomputed unless one of its arguments changes. • Selectors are composable. They can be used as input to other selectors. • (This list is from the official documentation.)

Slide 95

Slide 95 text

@stevekinney Chapter Five Ship Less Code

Slide 96

Slide 96 text

@stevekinney Parsing and compiling can take up the lion’s share of your application’s load time.

Slide 97

Slide 97 text

@stevekinney

Slide 98

Slide 98 text

@stevekinney Corollary: If you ship less code, then you’ll have less to parse and compile.

Slide 99

Slide 99 text

@stevekinney Lazy-loading and pre-loading with React and webpack

Slide 100

Slide 100 text

@stevekinney And now: A review of Steve’s golden rules for performance.

Slide 101

Slide 101 text

@stevekinney Not doing stuff is faster than doing stuff.

Slide 102

Slide 102 text

@stevekinney Doing stuff later is a way to not do stuff now. So, it’s faster.

Slide 103

Slide 103 text

@stevekinney

Slide 104

Slide 104 text

@stevekinney

Slide 105

Slide 105 text

@stevekinney

Slide 106

Slide 106 text

@stevekinney import React, {lazy, Suspense} from 'react'; const OtherComponent = lazy(() => import('./OtherComponent')); const MyComponent = () => { return ( Loading ... }> ); }

Slide 107

Slide 107 text

@stevekinney import Loadable from 'react-loadable'; const OtherComponent = Loadable({ loader: () => import('./OtherComponent'), loading:
Loading ...
, }); const MyComponent = () => { return (
); }

Slide 108

Slide 108 text

@stevekinney react-lazyload import React from 'react'; import LazyLoad from 'react-lazyload'; import MyComponent from './MyComponent'; const App = () => { return (
); };

Slide 109

Slide 109 text

@stevekinney Takeaways • Some libraries have code you don’t need. See if you can get that out of your build. • Get the code you need now now. • Get the code you need later later. • Your tools can help you do this.

Slide 110

Slide 110 text

@stevekinney Chapter Six Using Build Tools to Do Hard Work

Slide 111

Slide 111 text

@stevekinney And now: Steve’s Silver Rule of Performance

Slide 112

Slide 112 text

@stevekinney Let your build tools do boring stuff for you.

Slide 113

Slide 113 text

@stevekinney

Slide 114

Slide 114 text

@stevekinney Let’s file these under “maybe use.”

Slide 115

Slide 115 text

@stevekinney npm install @babel/plugin- transform-react-remove-prop-types

Slide 116

Slide 116 text

@stevekinney import React from 'react'; import PropTypes from 'prop-types'; class Announcement extends React.Component { render() { return (

Important Announcement

{this.props.content}

) } } Announcement.propTypes = { content: PropTypes.string, } export default Announcement;

Slide 117

Slide 117 text

@stevekinney import React from 'react'; import PropTypes from 'prop-types'; class Announcement extends React.Component { render() { return

Important Announcement

{this.props.content}

; } } export default Announcement;

Slide 118

Slide 118 text

@stevekinney { "plugins": [ "syntax-jsx", [ "transform-react-remove-prop-types", { "removeImport": true } ] ] }

Slide 119

Slide 119 text

@stevekinney import React from 'react'; class Announcement extends React.Component { render() { return

Important Announcement

{this.props.content}

; } } export default Announcement;

Slide 120

Slide 120 text

@stevekinney { "plugins": [ "syntax-jsx", [ "transform-react-remove-prop-types", { "mode": "wrap" } ] ] }

Slide 121

Slide 121 text

@stevekinney Announcement.propTypes = process.env.NODE_ENV !== "production" ? { content: PropTypes.string } : {};

Slide 122

Slide 122 text

@stevekinney npm install babel-plugin- transform-react-pure-class-to- function

Slide 123

Slide 123 text

@stevekinney import React from 'react'; class Announcement extends React.Component { render() { return (

Important Announcement

{this.props.content}

) } } export default Announcement;

Slide 124

Slide 124 text

@stevekinney import React from 'react'; function Announcement(props) { return

Important Announcement

{props.content}

; } export default Announcement;

Slide 125

Slide 125 text

@stevekinney What if the component has state?

Slide 126

Slide 126 text

@stevekinney class Counter extends React.Component { constructor() { super(); this.state = 0; } render() { return

{this.state.count}

; } }

Slide 127

Slide 127 text

@stevekinney class Counter extends React.Component { constructor() { super(); this.state = 0; } render() { return

{this.state.count}

; } }

Slide 128

Slide 128 text

@stevekinney Why is this cool?

Slide 129

Slide 129 text

@stevekinney npm install @babel/plugin- transform-react-constant-elements

Slide 130

Slide 130 text

@stevekinney import React from 'react'; class Article extends React.Component { render() { return (

Important Announcement

{this.props.content}

—The Management ) } } export default Announcement;

Slide 131

Slide 131 text

@stevekinney import React from 'react'; var _ref =

Important Announcement

; var _ref2 = —The Management ; class Announcement extends React.Component { render() { return {_ref}

{this.props.content}

{_ref2} ; } } export default Announcement;

Slide 132

Slide 132 text

@stevekinney

Slide 133

Slide 133 text

@stevekinney for (let x = 0; x < 10; x ++) { console.log(x); }

Slide 134

Slide 134 text

@stevekinney console.log(0); console.log(1); console.log(2); console.log(3); console.log(4); console.log(5); console.log(6); console.log(7); console.log(8); console.log(9);

Slide 135

Slide 135 text

@stevekinney (function () { function fibonacci(x) { return x <= 1 ? x : fibonacci(x - 1) + fibonacci(x - 2); } global.x = fibonacci(10); })();

Slide 136

Slide 136 text

@stevekinney

Slide 137

Slide 137 text

@stevekinney Appendix A Conclusion of Sorts

Slide 138

Slide 138 text

@stevekinney Some things to think about… • Are we testing performance on fancy MacBook Pros or consumer-grade hardware? • Are we simulating less-than-perfect network conditions. • What is our performance budget?

Slide 139

Slide 139 text

@stevekinney In Review • Build for production. • Profile first! • Don’t check components for updates if you can help it. • Prefer primitives. • Keep your state lean and mean.

Slide 140

Slide 140 text

@stevekinney Thank You! @stevekinney https://bit.ly/electronjs