$30 off During Our Annual Pro Sale. View Details »

React Performance

Steve Kinney
December 04, 2018

React Performance

Steve Kinney

December 04, 2018
Tweet

More Decks by Steve Kinney

Other Decks in Technology

Transcript

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

    View Slide

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

    View Slide

  3. @stevekinney

    View Slide

  4. @stevekinney

    View Slide

  5. @stevekinney
    We’re going to think about
    performance.

    View Slide

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

    View Slide

  7. @stevekinney
    Why does performance
    matter?

    View Slide

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

    View Slide

  9. @stevekinney
    Actually, I’m not…

    View Slide


  10. @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

    View Slide


  11. @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

    View Slide


  12. @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

    View Slide

  13. @stevekinney

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  20. @stevekinney
    One more thing…

    View Slide

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

    View Slide

  22. @stevekinney
    Thinking About Performance

    View Slide

  23. @stevekinney

    View Slide

  24. @stevekinney

    View Slide

  25. @stevekinney
    There are different kinds of
    performance.

    View Slide

  26. @stevekinney

    View Slide

  27. @stevekinney

    View Slide


  28. @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

    View Slide

  29. @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?

    View Slide

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

    View Slide

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

    View Slide

  32. @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.

    View Slide

  33. @stevekinney
    What are you sacrificing?
    Readability? Reliability?

    View Slide

  34. @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.

    View Slide

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

    View Slide

  36. @stevekinney
    Doing Less Stuff Takes Less
    Time.

    View Slide

  37. @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.

    View Slide

  38. @stevekinney
    Chapter Two
    Who Hurt You?

    View Slide

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

    View Slide

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

    View Slide

  41. @stevekinney
    ?react_perf

    View Slide

  42. @stevekinney

    View Slide

  43. @stevekinney

    View Slide

  44. @stevekinney

    View Slide

  45. @stevekinney
    Chapter Three
    Optimizing React
    Performance

    View Slide

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

    View Slide

  47. @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.

    View Slide

  48. @stevekinney

    View Slide

  49. @stevekinney

    View Slide

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

    View Slide

  51. @stevekinney
    Question: What makes React
    fast?

    View Slide

  52. @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.

    View Slide

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

    View Slide

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

    View Slide

  55. @stevekinney
    shouldComponentUpdate()

    View Slide

  56. @stevekinney

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  60. @stevekinney

    View Slide

  61. @stevekinney
    Pure Components

    View Slide

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

    View Slide

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

    View Slide

  64. @stevekinney
    A Word of Warning

    View Slide

  65. @stevekinney

    View Slide

  66. @stevekinney

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  70. @stevekinney

    const Posts = (posts) => (

    { posts.map((post, index) => ) }

    );
    const Posts = (posts) => (

    { posts.map((post) => ) }

    );

    View Slide

  71. @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.

    View Slide

  72. @stevekinney
    Chapter Four
    State Management

    View Slide

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

    View Slide

  74. @stevekinney
    Prefer Primitives

    View Slide

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

    View Slide

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

    View Slide

  77. @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;

    View Slide

  78. @stevekinney
    Keep Your Data Flat

    View Slide

  79. @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.' };

    View Slide

  80. @stevekinney
    posts === posts;

    View Slide

  81. @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?
    }
    },
    },
    },
    },
    // …
    };

    View Slide

  82. @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?'
    }
    }
    }
    }
    }
    });

    View Slide

  83. @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',
    },
    // …
    },
    };

    View Slide

  84. @stevekinney
    paularmstrong/normalizr

    View Slide

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

    View Slide

  86. @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);

    View Slide

  87. @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" }
    }
    }
    }

    View Slide

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

    View Slide

  89. @stevekinney
    Pro Tip: Break up your
    containers.

    View Slide

  90. @stevekinney
    Me, like a year or two ago…
    const PostsList = posts => (

    {posts.map(post => (

    ))}

    );
    const PostsListContainer = connect(posts => posts);

    View Slide

  91. @stevekinney
    Me, today…
    const PostsList = (ids) => (

    {ids.map(id => (

    ))}

    );

    View Slide

  92. @stevekinney
    Memoize Your Containers

    View Slide

  93. @stevekinney
    Reselect

    View Slide

  94. @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.)

    View Slide

  95. @stevekinney
    Chapter Five
    Ship Less Code

    View Slide

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

    View Slide

  97. @stevekinney

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  101. @stevekinney
    Not doing stuff is faster than
    doing stuff.

    View Slide

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

    View Slide

  103. @stevekinney

    View Slide

  104. @stevekinney

    View Slide

  105. @stevekinney

    View Slide

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


    );
    }

    View Slide

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



    );
    }

    View Slide

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











    );
    };

    View Slide

  109. @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.

    View Slide

  110. @stevekinney
    Chapter Six
    Using Build Tools to Do Hard
    Work

    View Slide

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

    View Slide

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

    View Slide

  113. @stevekinney

    View Slide

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

    View Slide

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

    View Slide

  116. @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;

    View Slide

  117. @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;

    View Slide

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

    View Slide

  119. @stevekinney
    import React from 'react';
    class Announcement extends React.Component {
    render() {
    return
    Important Announcement
    {this.props.content}
    ;
    }
    }
    export default Announcement;

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    Important Announcement
    {this.props.content}

    )
    }
    }
    export default Announcement;

    View Slide

  124. @stevekinney
    import React from 'react';
    function Announcement(props) {
    return
    Important Announcement
    {props.content}
    ;
    }
    export default Announcement;

    View Slide

  125. @stevekinney
    What if the component has
    state?

    View Slide

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

    View Slide

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

    View Slide

  128. @stevekinney
    Why is this cool?

    View Slide

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

    View Slide

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

    Important Announcement
    {this.props.content}
    —The Management

    )
    }
    }
    export default Announcement;

    View Slide

  131. @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;

    View Slide

  132. @stevekinney

    View Slide

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

    View Slide

  134. @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);

    View Slide

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

    View Slide

  136. @stevekinney

    View Slide

  137. @stevekinney
    Appendix
    A Conclusion of Sorts

    View Slide

  138. @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?

    View Slide

  139. @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.

    View Slide

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

    View Slide