I took a TodoList example to reflect my problem but obviously my real-world code is more complex.
I have some pseudo-code like this.
var Todo = React.createClass({
mixins: [PureRenderMixin],
............
}
var TodosContainer = React.createClass({
mixins: [PureRenderMixin],
renderTodo: function(todo) {
return <Todo key={todo.id} todoData={todo} x={this.props.x} y={this.props.y} .../>;
},
render: function() {
var todos = this.props.todos.map(this.renderTodo)
return (
<ReactCSSTransitionGroup transitionName="transition-todo">
{todos}
</ReactCSSTransitionGroup>,
);
}
});
All my data is immutable, and PureRenderMixin is used appropriately and everything works fine. When a Todo data is modified, only the parent and the edited todo is re-rendered.
The problem is that at some point my list grows big as the user is scrolling. And when a single Todo is updated, it takes more and more time to render the parent, call shouldComponentUpdate
on all the todos, and then render the single todo.
As you can see, the Todo component has other component than the Todo data. This is data that is required for render by all the todos and is shared (for example we could imagine there's a "displayMode" for the todos). Having many properties makes the shouldComponentUpdate
perform a little bit slower.
Also, using ReactCSSTransitionGroup
seems to slow down a little too, as ReactCSSTransitionGroup
have to render itself and ReactCSSTransitionGroupChild
even before the shouldComponentUpdate
of todos is called. React.addons.Perf
shows that ReactCSSTransitionGroup > ReactCSSTransitionGroupChild
rendering is time wasted for each item of the list.
So, as far as I know, I use PureRenderMixin
but with a larger list this may be not enough. I still get not so bad performances, but would like to know if there are easy ways to optimize my renderings.
Any idea?
Edit:
So far, my big list is paginated, so instead of having a big list of items, I now split this big list in a list of pages. This permits to have better performances as each page can now implement shouldComponentUpdate
. Now when an item changes in a page, React only has to call the main render function that iterates on the page, and only call the render function from a single page, which make a lot less iteration work.
However, my render performance is still linear to the page number (O(n)) I have. So if I have thousands of pages it's still the same issue :) In my usecase it's unlikely to happen but I'm still interested in a better solution.
I am pretty sure it is possible to achieve O(log(n)) rendering performance where n is the number of items (or pages), by splitting a large list into a tree (like a persistent data structure), and where each node has the power to short-circuit the computation with shouldComponentUpdate
Yes I'm thinking of something akin to persistent data structures like Vector in Scala or Clojure:
However I'm concerned about React because as far as I know it may have to create intermediate dom nodes when rendering the internal nodes of the tree. This may be a problem according to the usecase (and may be solved in future versions of React)
Also as we are using Javascript I wonder if Immutable-JS support this, and make the "internal nodes" accessible. See: https://github.com/facebook/immutable-js/issues/541
Edit: useful link with my experiments: Can a React-Redux app really scale as well as, say Backbone? Even with reselect. On mobile
See Question&Answers more detail:
os