Skip to content

Instantly share code, notes, and snippets.

@jslatts
Last active November 14, 2024 16:58
Show Gist options
  • Save jslatts/1c5d4d46b6e5b0ac0e917fa3b6f7968f to your computer and use it in GitHub Desktop.
Save jslatts/1c5d4d46b6e5b0ac0e917fa3b6f7968f to your computer and use it in GitHub Desktop.
Utility to bind selectors to a slice of state. Helpful for keeping things DRY when colocating selectors and reducers
// Example of usage
import { selectors } from './rootReducer';
import { selectors } from '../../reducers/rootReducer';
const mapStateToProps = (state: State, ownProps: any) => ({
theseObjects: selectors.getTheseObjects(state),
thoseObjects: selectors.getThoseObjects(state),
showSomethinginUi: selectors.getSomethingFromUiSelectors(state),
});
... MORE CODE ...
// @flow
// Helper to wrap a redux selector with a predetermined state slice function
'use strict';
export const bindSelectors = (slicer: (any) => any, selectors: *) => {
const keys = Object.keys(selectors);
const boundMethods = {};
keys.forEach(k => {
boundMethods[k] = fullState => selectors[k](slicer(fullState));
});
return boundMethods;
};
export default bindSelectors;
// Example root reducer
import { bindSelectors } from './bindSelectors';
import ui, { selectors as uiSelectors } from './ui';
import theseObjects, { selectors as theseObjectSelectors } from './theseObjectsReducers';
import thoseObjects, { selectors as thoseObjectSelectors } from './thoseObjectsReducers';
// State shape looks like
const defaultState = {
ui,
theseObjects,
thoseObjects,
};
...
REDUCER CODE GOES HERE
...
export default rootReducer;
// bindSelectors binds a state slice to the sub-reducer's selectors
export const selectors = {
...bindSelectors(state => state.theseObjects, theseObjectSelectors),
...bindSelectors(state => state.thoseObjects, thoseObjectSelectors),
...bindSelectors(state => state.ui, uiSelectors),
};
// Example theseObjects reducer
...
REDUCER CODE GOES HERE
...
export default theseObjects;
// Seletors in this file do not have to worry about what the state of the whole state tree looks like
// They just operate within the same scope of their corresponding reducers
export const selectors = {
getFilteredObjects: (state: State) => state.filter(t => t.someFlag === true),
};
@SteveByerly
Copy link

@jslatts the selectors object that is exported from rootReducer.js could introduce a subtle bug if two slices of state define selectors with the same name.

Instead of spreading all selectors for each slice into a single object, you might consider namespacing them

export const selectors = {
  theseObjects: bindSelectors(state => state.theseObjects, theseObjectSelectors),
  thoseObjects: bindSelectors(state => state.thoseObjects, thoseObjectSelectors),
  ui: bindSelectors(state => state.ui, uiSelectors),
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment