Hacker News new | comments | show | ask | jobs | submit login
Performance Engineering with React, Part 1 (benchling.engineering)
158 points by sajithw 533 days ago | hide | past | web | 38 comments | favorite



I've been using React to make a 60fps music game, and this article covers basically all of the issues I ran into:

- Originally, the game was rendered with SVG elements, so it was very important to use the shouldComponentUpdate hook to ensure elements weren't rendering unnecessarily (and using CSS transformations to position them where possible, as that's way faster than updating SVG coordinates!)

- I had something similar to the function.bind problem since some of my components had a map of hotkey handlers that I was (naively) returning from a `getHotKeyHandlers` function, meaning the handlers were redefined every render.

- I wasn't caching/memoizing calculations done to the Redux state as well as I should have (Reselect is very useful for this!)

I ended up deciding SVG wasn't fast enough for my use case and rewriting the core game loop rendering to use Canvas, which was so much faster that I could be a lot lazier about optimizing things. Everything around the canvas is still rendered through React components (i.e. in game UI and menu screens), and the state is still handled through Redux. It's a very fun way to write a game :)


Do you think React Canvas is a thing you could have used?

https://github.com/Flipboard/react-canvas

I've still not had a chance to play around with it, but the Flipboard mobile site looks phenomenal.


That looked interesting but I didn't need the most important functionality it provides (click/touch handling, fancy text rendering, etc) so I stuck with just using the Canvas API. It's pretty simple to do in React: https://gist.github.com/thomasboyt/bcc496c823a01ac5f648


I might be missing something here: what benefit does React bring in this example? If you're using canvas to render without any kind of virtual DOM, why use React at all?


Can't you combine? Thinking it could be used for UI. Makes sense when you need more support, like localisation and other builtin stuff. Just guessing.


Oh, nothing on its own in that tiny example. In practice, it's just a component that could be used in a larger application and can be easily tied into the same rendering and state trees.


From my understanding, React Canvas handles which parts to redraw for you. Redrawing the entire Canvas with each frame is not performant.


Canvas is nice... until you need to embed a video into it.


cant you avoid that by using a video element, and placing it over the canvas with CSS?


Not if you use clipping-paths in the canvas, or if you want to partially overlay the video with canvas elements.


Thanks for the valuable tips!

Currently, my biggest 'bottleneck' when developing React applications is managing complex forms. Right now I'm using redux-form [0] which is a great tool, but not very performant on dynamic deep/complex forms (more info and partial hacks here: [1]).

Other than that, I don't have performance problems, but I'm curious when will I hit Redux limitations (single store sounds great from the conceptual point of view, but I'm not sure about the performance one). Also, can second ImmutableJS recommendation, after a while of using it's completely natural to use.

[0] https://github.com/erikras/redux-form/

[1] https://github.com/erikras/redux-form/issues/529


I've also hit the same problems with redux-form, though it does provide a great interface - thanks for the link to that issue!

I use redux on our newest application and so far have been very happy with it. Previously I was using the `flux` facebook library. Flux was also good, but there was a lot of boilerplate for creating new stores.

Redux is nice because your 'containers' (smart-components that `connect` to the store via `react-redux`) only consume the slice of state that they need. My store is pretty big, but the Settings page, for example, only needs the userId from the store. Redux optimizes for that. The single-store has actually greatly simplified the whole process without any noticeable performance hits.


> single store sounds great from the conceptual point of view, but I'm not sure about the performance one

But the Redux "single store" is actually a composition of multiple stores, each with its own reducer function. Performance-wise, it's actually better than vanilla Flux, because, when an action causes N stores to change with vanilla Flux, you can get up to N renders of your components (depending on how many stores they subscribe to). At least, with Redux there is only one render when the whole store has been updated.


When I was looking around and comparing I think most allowed for some form of aggregation and syncing them into one call. Not sure though. Still learning.


The biggest performance issue I've encountered is with long lists. The most basic implementation means you will have to instantiate a new component for every item in the list even if only one of them has changed. I haven't spent that much time looking into it, so the bottleneck might be function binding (I hadn't thought of that before) but if anybody has an approach to this any ideas are appreciated. Maybe only pass an id as a prop and grab the rest from context?


I'm going to cover this in my next post, but I've found that using PureRenderMixin with fairly large number of items (~100s) works. The non-trivial step is often identifying why your subcomponents are still re-rendering, which I solved by writing a mixin that logs to the console what props/state is deep equal but not shallow equal (i.e. where PureRenderMixin should theoretically work).

But I agree, it's still not perfect. You avoid doing the more expensive virtual DOM comparison, but you still have to iterate over all the items to do the PureRenderMixin checks. And it is still O(n) time, just with a smaller constant.

Another solution is to not actually render all the children, just the ones that are visible on the screen, i.e. using some React implementation of infinite lists (see: https://facebook.github.io/fixed-data-table/ )

Another idea that I've been thinking about is to arbitrarily fragment your list into lists (possibly of fixed size, or a fixed number of fragments). Each fragment gets its own subcomponent, so we can avoid rendering fragments that haven't changed. For example, instead of a list of 100 items, we treat it as 10 lists of 10 items. If we change one item, we end up instantiating 10 components, and then recursing into the single fragment that has changed, instead of over all 100 items.


I've thought about doing the list of smaller lists but it could cause issues with the DOM (if you care about it) since components must return a single element. Will look into that fixed data table.


See also react-list (https://github.com/orgsync/react-list), which does a surprisingly good and hassle-free job of rendering partial lists.


I want to see an article which shows how to code a fast rich text editor in React.

I tried to implement one by representing the text as a long list of character components, thinking react could handle this by only executing small incremental updates, but the whole thing became slow anyway.

Am I missing something, or is react not up to this task?


Building a fast rich text editor in React is no easy task, and would be at least a series of articles :-P. At Benchling we've built a fairly fast rich text editor in React--but it was a considerable amount of work and still requires quite a bit more work. You may have heard that Atom was originally written in React, but they abandoned it to avoid its overhead[0]

There are open-source projects worth looking at like Ritzy[1] which is quite fast and written in React from what I've read.

As per your specific issue, I've found that using PureRenderMixin with each line of text represented as a component works for hundreds of lines, but this still ends up with performance issues as you scale to thousands of lines. Using a list of one-character components will run into performance issues much sooner as there is per-component overhead.

[0] https://github.com/atom/atom/pull/5624 [1] http://ritzyed.github.io/ritzy/


Maybe you could throttle, only take distinct changes and flatten into render? ;)


I don't see enough mention of this, but if you want immutability (to make shouldComponentUpdate easy) icepick is a better choice than Immutable.js for a great many applications:

https://github.com/aearly/icepick

It's tiny but offers a number of utilities that are more convenient than what you get with ES6 spread syntax. The tradeoff with Immutable.js is that if you have huge single objects (arrays with many elements, objects with many keys), then making copies will get more expensive than with Immutable. Otherwise, icepick is all win.


Author here, happy to answer any questions!


With the ES6 class syntax, is it possible to avoid the function.bind/anonymous function issue (and subsequent need for the whole IntermediateBinder thing) by redefining methods that will be passed into a subcomponent in the constructor to their bound equivalent? That is, in the constructor: `this.foo = this.foo.bind(this)` and in render: `<SubComponent foo={this.foo} />`?


What you described is the current replacement for auto-bind (that React.createClass used to do for you). The issue arises when you have not just one SubComponent, but n SubComponents that require the parent method bound to the subcomponent's index, e.g. you have a deleteItemAtIndex method, and you only want to expose {deleteItem: deleteItemAtIndex.bind(this, i)} to each child component. ES6 React classes don't have any special way of handling this.

To re-iterate the possible solutions I've considered:

- Don't pass in a bound method; give the component the unbound method and its index and the child component can call it with the index passed in itself. - Generalize this to a re-usable intermediary component that does for you (it does feel a bit dirty) - Write your own bind function that annotates the bound function with original function + params allowing you to do a "deep" equality check, and then use a variant of PureRenderMixin that does this "deep" equality check.

Honestly, all of the solutions feel a bit hacky, but I've gravitated towards the first and second options.


Ah, of course. Thank you for the clarification.


Try this: `class extends Component { foo = () => { console.log('this was bound at construction time'); }; render() { return <SubComponent foo={this.foo} />; } }` This only works if you have stage-1 class properties enabled though.


I have a question not related to the performance: can you share more details about your UI stack and dev workflows (do you use something more than e.g: webpack and babel)? Looking at the screenshots at the homepage [0], your app looks both complex and cool :). Also any recommendations for performant charting/graphing library that plays well with React?

What do you use for state management? Flux, alt, redux, something else?

Thank you for sharing your experience and tips regarding developing React apps!

[0] https://benchling.com/


> can you share more details about your UI stack and dev workflows

Our front-end stack was primarily just webpack (no JSX because CoffeeScript's terseness sufficed). We recently started the migration to using Babel with ES6 and JSX because of the linting / tooling / community behind them.

I will be sharing a more in-depth look at my debugging workflow in the next post, so be on the lookout :-)

> Also any recommendations for performant charting/graphing library that plays well with React?

I haven't used many charting / graphing libraries, sorry!

> What do you use for state management?

We were fairly early adopters to React and Flux, so we have our own internal implementation of Flux. I haven't worked too much with the other state management solutions, but many of them seem like good, opinionated architectures.

> Thank you for sharing your experience and tips regarding developing React apps!

No problem :-)


You might consider ZingChart[0] for your charting/graphing needs. I've found it to be performant with large datasets and have had some success pairing it with React by writing a wrapper along the lines of https://github.com/zingchart/ZingChart-React/ which unfortunately wasn't suitable for our use case. I'd consider open sourcing the code but don't have the time to clean it up at present.

[0] http://www.zingchart.com/


Hi there! I'm the author who wrote that react wrapper and am interested in how it didn't fit your use case and what alternative you came up with. Feedback would be extremely helpful in improving the wrapper! Shoot me an email at mschultz@zingchart.com if you're interested.


These will _probably_ be useful but I did not understand any of the terms mentioned in the article. I guess I haven't hit any major bottlenecks yet. Bookmarked!


I just went through a rather large profiling/optimization run on the my site, BitMEX (we're a Bitcoin derivatives exchange). Performance is paramount in trading, so our site has to be fast. Here's some additional tips:

0. shouldComponentUpdate (SCU) is everything. Consider creating a base React.Element extension class. I define a base shouldComponentUpdate and some useful utilities on it. Having it in one place is really useful because you can log its output from every component and watch how your whole application updates. It makes it easy to see useless rerenders.

- One very helpful addition in our app has been a static 'SCUSelector' property, which tells our shallow shouldComponentUpdate to ignore certain props/state/context. This helps in combination with rich componentWillMount() or getInitialState() functions to precalculate expensive data that updates rarely/never.

1. It seems the `connect(component, selector)` model [0] encouraged by Redux to hook up application data to deep(er) components is much better for performance. You want to avoid renders in as many tree branches as possible. To this day, the root element (`<App>`) still holds most of our data and fans it out to the Router, Header, Sidebar, etc. This root-level render must be extremely fast because it runs on every tick. Hoisting any and all data that can be made constant is a huge help.

2. We saw massive (50%+) performance gains from Babel's constant-elements and inline-elements optimizations [1]. Careful with it - there's is a nasty bug [2] that only manifests in production you need to be aware of if you're deploying this.

3. Immutable-JS is kind of a wash. I'm not a big fan of the API or how easy it is to nest mutable objects and arrays inside immutables. The new array/object spread syntax in ES6 makes it easier to construct unique objects every time data updates, so you can do a shallow shouldComponentUpdate.

4. Watch out for function identity because it will blow SCU [3]. Either ignore the property explicitly, or use a binding helper like the one I posted on that issue [4].

5. If you're doing expensive data computation, such as update/insert/delete/image on websockets, profile it. If you're accidentally causing deopt, you'll feel it. Make sure the libraries you use don't cause deopt either. A few simple tweaks [5] can give you 2-10x performance boosts on hot functions [6].

6. If you haven't already, watch videos on how V8 optimizes. It will help you write fast code in the future.

7. Keep watching your app! The new React devtools can highlight components as they update. This is really useful to see what work is being done and how you can improve.

---

Links:

0. https://github.com/rackt/react-redux/blob/master/docs/api.md...

1. https://babeljs.io/docs/plugins/transform-react-inline-eleme...

2. https://github.com/facebook/react/issues/5138

3. https://github.com/facebook/react/issues/5197

4. https://github.com/facebook/react/issues/5197#issuecomment-1...

5. https://github.com/petkaantonov/bluebird/wiki/Optimization-k...

6. https://github.com/AmpersandJS/ampersand-state/pulls?utf8=%E...


> This post is for those of you with a complex React application. If you're building something smaller, you might not need to focus on performance yet. Don't prematurely optimize! Go build things!

This is bit meta, but it's really, really awesome to see an article on performance begin with this. Well done author :-)

It instantly inspires confidence that the material to follow is worthwhile reading. Knowing the author has started out with this philosophy I trust more that what's covered will focus on things that actually matter, rather than just being a crawl through every and any technique or quirk that they've come across, regardless of its relative impact.

Performance is one of those things where more is obviously better, all other things equal, but that last 20% of effort invested is almost always time that could be better spent improving other aspects of the average use case of any given product, let alone the average product.


TLDR for ClojureScript users: most techniques described here do not apply to you. Whatever React library you use (Rum, Om, Reagent, or other), most work has already been done for you through immutable data structures. Unless you go out of your way to create lots of components that react to something different than their parameters, shouldComponentUpdate will be handled quietly behind your back and you'll get impressive performance right out of the box. And no need to even learn about the differences between deep/shallow comparisons, either.


This is true unless you're using cljs-datetime. The library's use of goog.date objects under the hood means that storing one in your state tree invalidates the fast equality checks that everything in the clojurescript space is assuming.


[flagged]


There were no comments here...then you started a JSX debate...


Just trying to head 'em off at the pass.




Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | DMCA | Apply to YC | Contact

Search: