Imagine we have a reducer to control a list of items:
function listOfItems(state: Array<Object> = [], action: Object = {}): Array<Object> {
switch(action.type) {
case 'SHOW_ALL_ITEMS':
return action.data.items
default:
return state;
}
}Where Items looks like this:
type ItemType = {
id: string,
text: string,
completed: boolean
};Today we mapStateToProps for all incomplete items like this:
function mapStateToProps(state) {
return {
incompleteItems: state.listOfItems.filter((item) => {
return !item.completed
});
}
}There are a couple problems with this approach as the application grows.
- If the implementation of
incompleteItemsmay change. - Computation logic occurs in mapStateToProps
- Can't memoize the values of
incompleteItems
After talking with Dan Abramov (founder of Redux) he has been preaching the colocation of functions called selectors.
What is a selector?
- Selectors can compute derived data, allowing Redux to store the minimal possible state.
- Selectors are composable. They can be used as input to other selectors.
Let's turn our filter into a selector.
function getIncompleteItems(state) {
return state.listOfItems.filter((item) => {
return !item.completed
});
}And we update our mapStateToProps function:
function mapStateToProps(state) {
return {
incompleteItems: getIncompleteItems(state)
}
}Now we can reuse this logic across many components mapping this exact state! We can unit test it as well! More importantly we can now memoize this state with reselect