Description
Context
Today there was some discussions about the new redux tree view example and this SO question:
http://stackoverflow.com/questions/34981924/performance-issues-with-a-tree-structure-and-shouldcomponentupdate-in-react-re
As I pointed out the solution of Redux examples is fine enough for most usecases but does not scale it the number of nodes is very big. We agree that it's not a good idea in the first place to render a huge list or a huge tree, but it seems the author of the question is still interested in a solution. I don't know if the usecase is valid or an antipattern but I guess it's worth giving the opportunity to render a huge number of items in an efficient way
Solution
As described in my answer if the number of connected items grows too big, then every state change triggers all connected HOC's subscriptions so it does not scale well.
If we want to make it scale better, the HOC subscriptions should only be called when necessary.
If the state slice of node with id=1 is updated, it produces overhead to actually trigger the subscription of the HOC that connects data to node with id=2, because obviously the connected component is not interested at all in this change.
Redux store has a store.subscribe(listener)
method, and it is called inside connect
One can easily create a store enhancer that exposes a method like store.subscribeNode(nodeId,listener)
, which is only triggered when that node, with the provided, id is called. There are multiple ways of triggering this listener efficiently.
The problem is that once we have built this custom optimized subscription system, there's no way to reuse connect
to listen to it because the call to store.subscribe
is hardcoded here:
trySubscribe() {
if (shouldSubscribe && !this.unsubscribe) {
this.unsubscribe = this.store.subscribe(::this.handleChange)
this.handleChange()
}
}
A flexible solution would be to let the user control hw to subscribe to the store himself, by using connect options for example:
const mapStateToProps = (state,props) => {node: selectNodeById(state,props.nodeId)}
const connectOptions = {
doSubscribe: (store,props) => store.subscribeNode(props.nodeId)
}
connect(mapStateToProps, undefined,connectOptions)(ComponentToConnect)
The default doSubscribe
implementation would be: doSubscribe: (store,props) => store.subscribe()
which keeps the current behavior unchanged.
There's one corner case I don't know yet how to solve is when the nodeId props changes over time. It would mean the HOC should then unsubscribe and then resubscribe for the new nodeId. In practice this seems farfetched to do so and I'm not sure it's really worth trying to solve this but if anyone has an idea...
I can make a PR for that, just tell me if it would be accepted or if it seems to much indirection and is not an usecase you want to support in redux.