Reselect Yourself

Mitch Masia
Jan 23, 2018 · 3 min read

Make developing with Redux even more glorious.

The Problem

If you’ve been developing with Redux, you’ve probably felt the pain of having a bunch of custom logic in your render function. Raise your hand if this rings a bell:

render() {
const { results, searchFilter } = this.props

const filteredResults = results.filter(item => item.name.toLowerCase().indexOf(searchFilter.toLowerCase()) !== -1)
return (...)
}

There are a few problems with the code above.

  • It couples your component directly to the structure of your data.
  • It runs on every. single. render.
  • Your component should not be responsible for filtering the data it receives.

If only there was a way to abstract this logic from our component!

Redux Selectors

What if we wrote a pure function that transformed the data we get from Redux and toss that processed data into our components?

Let’s mock out a reducer we may use to track some API request:

const REQUEST = 'REQUEST'
const SUCCESS = 'SUCCESS'
const FAILURE = 'FAILURE'
const FILTER = 'FILTER'
const initialState = {
isFetching: false,
results: [],
error: null,
searchFilter: '',
}
const sitesReducer = (_state = initialState, action) => {
const { type, payload } = action
switch (type) {
case REQUEST:
return {
..._state,
isFetching: true,
error: null,
}
case SUCCESS:
return {
..._state,
isFetching: false,
results: payload.results,
}
case FAILURE:
return {
..._state,
isFetching: false,
error: payload.error,
}
case FILTER:
return {
..._state,
searchFilter: payload.searchFilter,
}
default:
return _state
}
}

Now, let’s say we fire the following action:

store.dispatch({
type: SUCCESS,
payload: {
results: [
{ id: 1, name: 'Hexient', url: 'https://hexientlabs.com' },
{ id: 2, name: 'Monokai', url: 'https://monokaicode.co' },
{ id: 3, name: 'Google', 'url: 'https://google.com' },
{ id: 4, name: 'Facebook', url: 'https://facebook.com' }
],
},
})

Our Redux state would look like this:

{
sites: {
isFetching: false,
error: null,
results: [
{ id: 1, name: 'Hexient', url: 'https://hexientlabs.com' },
{ id: 2, name: 'Monokai', url: 'https://monokaicode.co' },
{ id: 3, name: 'Google', 'url: 'https://google.com' },
{ id: 4, name: 'Facebook', url: 'https://facebook.com' }
],
searchFilter: '',
},
}

Pretty standard, right?

Now let’s get down to business. Instead of putting our custom filtering logic in our render function, we can create a pure function (selector) called getFilteredSites :

const getFilteredSites = (results, searchFilter) => results.filter(item => item.name.toLowerCase().indexOf(searchFilter.toLowerCase()) !== -1)

Awesome! Now in our mapStateToProps , we simply call:

const mapStateToProps = state => ({
filteredResults: getFilteredSites(state.sites.results, state.sites.searchFilter),
})

And voila! Our component automagically gets the filtered sites. So this is already pretty sweet, but let’s make it wayyyy better.

Enter Reselect

Reselect has become the pseudo-standard in computing derived data for your components from your Redux store. Using reselect is awesome because it gives your selectors superpowers, namely memoization and composability.

Sidenote: This article isn’t meant to be an in-depth guide on how to use the library- for that, read the docs.

In the meantime, here’s how we may break down our selector function into reusable pieces to achieve maximum memoization and readability.

import { createSelector } from 'reselect'const getSites = state => state.sites// Memoized selector that only runs when value of 'getSites' changes
const getSitesResults = createSelector(
getSites,
sites => sites.results.map(item => item.toLowerCase())
)
// Memoized selector that only runs when value of 'getSites' changes
const getSitesSearchFilter = createSelector(
getSites,
sites => sites.searchFilter.toLowerCase()
)
// Filter comprised of multiple nested memoized selectors
const getFilteredSites = createSelector(
[getSitesResults, getSitesSearchFilter]
(results, searchFilter) => results.filter(item => item.name.indexOf(searchFilter) !== -1)
)

How modular, how beautiful. Plus now we have the added efficiency bonus that our selectors only recompute data when absolutely necessary.

And now for our brand new mapStateToProps :

const mapStateToProps = state => ({
filteredResults: getFilteredSites(state)
})

At Hexient Labs, we’re big fans of using reselect wherever possible. Hopefully this article gives you a little insight into a great way to reduce logic in your render methods and decouple your components from your data structures.


hexient-labs

Mitch Masia

Written by

Mitch is a developer, teacher, and entrepreneur building cool things at Guaranteed Rate.

hexient-labs

Tiny thoughts from the team at Hexient Labs

More From Medium

Related reads

Related reads

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade