Skip to content

reselect

Created: 2017-10-17 10:38:42 -0700 Modified: 2017-10-17 10:47:51 -0700

This is a library that lets you select data in React/Redux efficiently; it only recomputes when needed and memoizes the result.

HiDeoo: Adam13531 Yeah the selector is now the only way to get this part of the Redux data, in fact, in all my code, we only use selectors to access Redux, never an action

HiDeoo: Adam13531 It also adds a separations between actions (to modify the Redux state) & selectors (to access the Redux state) which is quite good imo

HiDeoo: Adam13531 One other extra bonus with selectors that you get for free is that it’s way easier to test them vs a Redux actions with dispatch, a mocked store & everything, you pass an input data & always return the same output data. It totally removes the Redux part of the equation for test & tests are Kreygasm

In Bot Land, I wanted to be able to only show the first modal dialog out of potentially many dialogs.

Originally, I had a solution like this (which worked):

// actions/modal.js

export function getCurrentModal() {

return (dispatch, getState) => {

const {modalModels} = getState().modalReducer;

const firstModalModel = _.head(modalModels);

if (!_.isNil(firstModalModel)) {

return ModalModel.deserializeFromRedux(firstModalModel);

}

return null;

};

}

// containers/modal.js

class Modal extends Component {

render() {

const modalModel = this.props.getCurrentModal();

if (_.isNil(modalModel)) {

return null;

}

return <ReactModal blahblahblah/>;

}

}

function mapStateToProps(state) {

return {

// This is only needed so that this class knows to rerender.

modalState: state.modalReducer

};

}

const dispatchToProps = {

getCurrentModal: ModalActions.getCurrentModal,

closeCurrentModal: ModalActions.closeCurrentModal

};

export default connect(mapStateToProps, dispatchToProps)(Modal);

The major issue is that the “render” function has a call to “getCurrentModal” which resides in “actions” despite that it’s not really an action. Also, never mind for now that there’s no shouldComponentUpdate based on that; I didn’t address that in the reselect solution below either.

Here is the reselect way of doing things:

// selectors/modal.js

import { createSelector } from ‘reselect’;

const _ = require(‘lodash’);

const getModals = (state, props) => {

const {modalModels} = state.modalReducer;

return modalModels;

};

/**

  • Creates a selector that returns the current modal dialog.

  • @return {?ModalModel}

*/

export const getCurrentModal = createSelector(

[ getModals ],

(modalModels) => {

const firstModalModel = _.head(modalModels);

if (!_.isNil(firstModalModel)) {

return firstModalModel;

}

return null;

}

);

// containers/modal.js

import { getCurrentModal } from ‘../selectors/modal’;

class Modal extends Component {

render() {

const modalModel = this.props.currentModal;

if (_.isNil(modalModel)) {

return null;

}

return <ReactModal blahblahblah/>;

}

}

function mapStateToProps(state) {

return {

// This is only needed so that this class knows to rerender.

modalState: state.modalReducer,

currentModal: getCurrentModal(state)

};

}

const dispatchToProps = {

closeCurrentModal: ModalActions.closeCurrentModal

};

export default connect(mapStateToProps, dispatchToProps)(Modal);

The important implementation details to note:

  • In the selector file, getModals just accesses the state. It doesn’t do anything else. The “computation” part is up to the selector, not an input function like getModals.