Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
React/Redux perf discussion

[10:57 PM] acemarke: so, the canonical "bad perf for React+Redux" example is a todo list of 10,000 items
[10:58 PM] Sinistral: I always thought passing an object through was just a typical JS pointer
[10:58 PM] acemarke: the trivially obvious store shape is a single array of all 10K todo objects
[10:58 PM] acemarke: with a single connected list component
[10:58 PM] Sinistral: eh, ignore me, finish
[10:59 PM] acemarke: the list's mapState retrieves the array, and the list component renders this.props.todos.map(todo => <TodoListItem todo={todo} /> )
[10:59 PM] acemarke: which works fine the first time around
[10:59 PM] acemarke: but if you edit, say, the "text" field in a single todo
[11:00 PM] acemarke: your reducer returns a new updated todo object inside of a new array reference, shallow-copied
[11:00 PM] Sinistral: You are re-drawing the enti...oh
[11:00 PM] Sinistral: OH
[11:00 PM] acemarke: and now the parent list pulls the new array ref, and re-renders
[11:00 PM] Sinistral: So by only updating ID's, you avoid this problem because the component is able to grab that data by itself
[11:00 PM] acemarke: causing all 10,000 children to re-render
[11:00 PM] acemarke: yup
[11:00 PM] acemarke: and while React's diffing is good, that's still a ton of unnecessary work
[11:01 PM] Sinistral: Right, even using keys it has limits because you still have N checks to make in the best case
[11:01 PM] acemarke: whereas if the todos are normalized into { todos : { byId : {}, allTodos : [ ] } }
[11:01 PM] acemarke: now you pull in the array of IDs
[11:01 PM] Sinistral: See, I lack a lot of context here because currently we have very little iterated content that is also editable
[11:01 PM] acemarke: if one todo entry changes, the array of IDs doesn't
[11:01 PM] Sinistral: but we will very soon
[11:02 PM] Sinistral: But yea, normalizr makes a ton more sense to me now
[11:02 PM] acemarke: instead of 10001 renders, you get 10001 mapState calls
[11:02 PM] acemarke: 10000 of them return the exact same data as last time, so connect() skips re-rendering
[11:02 PM] acemarke: the one connected <TodoItem /> re-renders
[11:03 PM] acemarke: and the benchmarks show that the cost of running those mapState calls and doing shallow-equal checks is much less than doing the VDOM diffing
[11:03 PM] Sinistral: I mean even so, thats a ton of logic you are running when you dont need to be
[11:03 PM] acemarke: this is also where the per-component-instance selector/memoization can come in
[11:03 PM] Sinistral: right
[11:04 PM] Sinistral: ReSelect makes SO much more sense to me now. Like, we have 1 or 2 use cases currently
[11:04 PM] acemarke: because if you really set things up well, you can effectively turn every mapState call into a couple quick memoized selector checks
[11:04 PM] Sinistral: but honestly it was hard finding use cases for it
[11:04 PM] Sinistral: but with this model it makes a ton more sense
[11:04 PM] Sinistral: I have been wondering for awhile why memoization was such a giant topic around react/redux, like I knew it's applications
[11:05 PM] Sinistral: but it never made sense to me why it was so much more prevelent
[11:05 PM] Sinistral: this was the piece I've been missing
[11:05 PM] acemarke: it all comes down to React's top-down aproach, and Redux's subscription notifications
[11:05 PM] Sinistral: right
[11:05 PM] acemarke: with top-down rendering, chopping out subtrees is a big deal
[11:05 PM] acemarke: and with O(n) subscription notifications, cutting down the amount of work per mapState call helps
[11:06 PM] Sinistral: Yea, but I wasn't picking up on the application of connecting everything because I was under the assumption the correct route was connecting only your top level main components(edited)
[11:06 PM] Sinistral: So I never explored the path of "what if"
[11:06 PM] acemarke: and now you know :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment