Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Document per-component memoization with React Redux #79

Closed
gaearon opened this issue Feb 4, 2016 · 11 comments
Closed

Document per-component memoization with React Redux #79

gaearon opened this issue Feb 4, 2016 · 11 comments
Labels

Comments

@gaearon
Copy link

gaearon commented Feb 4, 2016

React Redux added per-instance memoization in 4.3.0.

This approach, originally explained here (although the API changed), allows significant perf wins for Reselect when computed state props depend on component’s own props, as those are now memoized per instance.

I think this approach is worth explaining both here and in the Computing Derived Data recipe. I don’t have time to contribute this, but I’m sure it will be a very valuable addition.

cc @tgriesser who implemented the feature in React Redux

@ellbee
Copy link
Collaborator

ellbee commented Feb 4, 2016

Thanks @gaearon. There are a couple of other things I'd like to see too:

  • Updating the README to include the recent changes to the examples in the Computing Derived Data recipe.
  • There is an extra section that deals with passing props to a selector in the README that I think would be a good idea to update and copy back to the Computing Derived Data recipe. The example I used is contrived, so if someone would like to re-write it with a more realistic example (or just suggest a better idea) that would be great!

If someone wants to take this on then I would appreciate it. Make a comment here so people know you are working on it. If there are no takers I will pick it up at the start of next week.

@ellbee
Copy link
Collaborator

ellbee commented Feb 4, 2016

@tgriesser has taken on describing the new per-instance memoization feature, but the other two bullet points in the last comment still need attention.

@ellbee
Copy link
Collaborator

ellbee commented Feb 9, 2016

I am working on this now.

@ellbee ellbee added docs and removed help wanted labels Feb 9, 2016
@bramdevries
Copy link

@ellbee @tgriesser Do you have an update on the documentation? I'm trying to implement this into my app but the API is not very clear. Currently have it setup like this:

//./Selectors/NodeSelector
import {createSelector} from 'reselect'

const idSelector = (state, props) => props.id;
const treeSelector = state => state.project.get('tree');

export default createSelector(treeSelector, idSelector, (tree, id) => {
    id = String(id);
    return tree.has(id) ? tree.get(id).toJS() : {};
});
//./Components/Node
export default connect(() => {
    return NodeSelector;
})(Node);

The API for the Node component is as follows:

<Node id={1} />
<Node id={2} />
<Node id={3} />

In my current implementation I'm still seeing unnecessary updates.

@tgriesser
Copy link

@bramdevries the issue here is state.project.get('tree') is being updated, so the createSelector memoization fails. What you want is something like this:

//./Selectors/NodeSelector
import {createSelector} from 'reselect'

const treeItemSelector = (state, props) => (
  state.project.getIn(['tree', '' + props.id], Immutable.Map());
)

export default createSelector(treeItemSelector, (treeItem) => {
    return treeItem.toJS()
});

So the toJS is only run if the property at ['tree', '' + props.id] has changed.

@bramdevries
Copy link

@tgriesser I see, thanks for the snippet.

However, I'm not seeing the memoization take place, it's still rendering all the Node components when something in the state changes. This component does also uses react-dnd so the full export looks like this:

@DragSource(DragTypes.NODE, source.spec, source.collect)
@DropTarget(DragTypes.NODE, target.spec, target.collect, {
    arePropsEqual: shallowEqual,
})
class Node extends Component {}

export default connect(() => NodeSelector)(Node);

If I remove the @DragSource and @DropTarget the memoization works, so I'm guessing this doesn't work well when a component is composed of multiple HOC's ?

@bramdevries
Copy link

Turns out I completely missed this part of the document that explains this concept very well: https://github.com/reactjs/reselect/blob/master/README.md#sharing-selectors-across-multiple-components Working now.

@ellbee
Copy link
Collaborator

ellbee commented Mar 13, 2016

You didn't miss it, I've only just put it up! 🙂 Glad to hear that it helped!

@ellbee ellbee closed this as completed Mar 13, 2016
@courthead
Copy link
Contributor

The docs now say:

In order to share a selector across multiple VisibleTodoList components and retain memoization, each instance of the component needs its own private copy of the selector.

If you jump right to this section without reading the immediately preceding section, this sentence make it seem like this rule applies to all selectors. Should we clarify that this only applies to a selector that's called with changing props?

@ellbee
Copy link
Collaborator

ellbee commented Apr 4, 2016

Hi @courthead. I agree, it can't hurt to clarify if it is potentially unclear. If you'd like to submit a PR, that would be great!

@courthead
Copy link
Contributor

@ellbee Okay I took a shot: #107

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants