Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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 Allow me to use some slides with too many

    words and then read them too you. #thoughtleadership101
  2. “ @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
  3. “ @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
  4. “ @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
  5. @stevekinney Aberdeen Group found that a 1 second slow down

    resulted 11% fewer page views, 7% less conversion. Source.
  6. @stevekinney Akamai found that two-second delay in web page load

    time increase bounce rates by 103 percent. Source.
  7. @stevekinney Google found that a 2% slower page resulted in

    2% fewer searches, which means 2% fewer ads shown. Source.
  8. @stevekinney According to “research” , if you want user to

    feel like your site is faster than your competitors, you need to be 20% faster.
  9. “ @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
  10. @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?
  11. @stevekinney Thinking deeply about the architecture of your application is

    a better use of your time than micro-benchmarks.
  12. @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.
  13. @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.
  14. @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.
  15. @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.
  16. @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.
  17. @stevekinney class MyComponent extends React.Component { shouldComponentUpdate(nextProps, nextState) { return

    ( shallowCompare(this.props, nextProps) && shallowCompare(this.state, nextState); ); } }
  18. @stevekinney That annoying warning about omitting a key when iterating

    over a collection is actually pretty important…
  19. @stevekinney It helps React figure out whether this is something

    new or just an existing thing that’s moved.
  20. @stevekinney const Posts = (posts) => ( <section> { posts.map((post,

    index) => <Post key={index} content={post} />) } </section> ); const Posts = (posts) => ( <section> { posts.map((post) => <Post key={Math.randon()} content={post} />) } </section> );
  21. @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.
  22. @stevekinney How you manage state in your application can have

    some important implications on the performance of your application.
  23. @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;
  24. @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.' };
  25. @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? } }, }, }, }, // … };
  26. @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?' } } } } } });
  27. @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', }, // … }, };
  28. @stevekinney { "id": "123", "author": { "id": "1", "name": "Paul"

    }, "title": "My awesome blog post", "comments": [ { "id": "324", "commenter": { "id": "2", "name": "Nicole" } } ] }
  29. @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);
  30. @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" } } } }
  31. @stevekinney Me, like a year or two ago… const PostsList

    = posts => ( <section> {posts.map(post => ( <Post content={post} /> ))} </section> ); const PostsListContainer = connect(posts => posts);
  32. @stevekinney Me, today… const PostsList = (ids) => ( <section>

    {ids.map(id => ( <PostContainer id={id} /> ))} </section> );
  33. @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.)
  34. @stevekinney import React, {lazy, Suspense} from 'react'; const OtherComponent =

    lazy(() => import('./OtherComponent')); const MyComponent = () => { return ( <Suspense fallback={<div>Loading ... </div>}> <OtherComponent /> </Suspense> ); }
  35. @stevekinney import Loadable from 'react-loadable'; const OtherComponent = Loadable({ loader:

    () => import('./OtherComponent'), loading: <div>Loading ... </div>, }); const MyComponent = () => { return ( <div> <OtherComponent /> </div> ); }
  36. @stevekinney react-lazyload import React from 'react'; import LazyLoad from 'react-lazyload';

    import MyComponent from './MyComponent'; const App = () => { return ( <div className="list"> <LazyLoad height={200}> <img src="tiger.jpg" /> </LazyLoad> <LazyLoad height={200} offset={100}> <MyComponent /> </LazyLoad> <LazyLoad> <MyComponent /> </LazyLoad> </div> ); };
  37. @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.
  38. @stevekinney import React from 'react'; import PropTypes from 'prop-types'; class

    Announcement extends React.Component { render() { return ( <article> <h1>Important Announcement </h1> <p>{this.props.content} </p> </article> ) } } Announcement.propTypes = { content: PropTypes.string, } export default Announcement;
  39. @stevekinney import React from 'react'; import PropTypes from 'prop-types'; class

    Announcement extends React.Component { render() { return <article> <h1>Important Announcement </h1> <p>{this.props.content} </p> </article>; } } export default Announcement;
  40. @stevekinney import React from 'react'; class Announcement extends React.Component {

    render() { return <article> <h1>Important Announcement </h1> <p>{this.props.content} </p> </article>; } } export default Announcement;
  41. @stevekinney import React from 'react'; class Announcement extends React.Component {

    render() { return ( <article> <h1>Important Announcement </h1> <p>{this.props.content} </p> </article> ) } } export default Announcement;
  42. @stevekinney import React from 'react'; function Announcement(props) { return <article>

    <h1>Important Announcement </h1> <p>{props.content} </p> </article>; } export default Announcement;
  43. @stevekinney class Counter extends React.Component { constructor() { super(); this.state

    = 0; } render() { return <p>{this.state.count} </p>; } }
  44. @stevekinney class Counter extends React.Component { constructor() { super(); this.state

    = 0; } render() { return <p>{this.state.count} </p>; } }
  45. @stevekinney import React from 'react'; class Article extends React.Component {

    render() { return ( <article> <h1>Important Announcement </h1> <p>{this.props.content} </p> <footer>—The Management </footer> </article> ) } } export default Announcement;
  46. @stevekinney import React from 'react'; var _ref = <h1>Important Announcement

    </h1>; var _ref2 = <footer>—The Management </footer>; class Announcement extends React.Component { render() { return <article> {_ref} <p>{this.props.content} </p> {_ref2} </article>; } } export default Announcement;
  47. @stevekinney for (let x = 0; x < 10; x

    ++) { console.log(x); }
  48. @stevekinney (function () { function fibonacci(x) { return x <=

    1 ? x : fibonacci(x - 1) + fibonacci(x - 2); } global.x = fibonacci(10); })();
  49. @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?
  50. @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.