Last active
May 21, 2018 12:45
-
-
Save bstro/2366fc8fae5c99803f942baaef161aac to your computer and use it in GitHub Desktop.
Revisions
-
Brendan Stromberger revised this gist
May 21, 2018 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,4 +1,4 @@ This is a simple pattern I follow that helps maintain an easily accessible graph of all derived data flowing through a Redux application, and expose that derived data (as well as the Redux state itself) to the global window object in the browser's console. ### selectorMiddleware.js computes selectors and sets the derived data on the window object, visible at `window.selectors` -
Brendan Stromberger revised this gist
May 21, 2018 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -35,5 +35,5 @@ export const getArticle => createSelector( export const getActiveArticle = createSelector( getArticles, createMatchSelector({ path: '/articles/:articleId' }), // https://github.com/ReactTraining/react-router/blob/master/packages/react-router-redux/modules/selectors.js ((articles, match) => articles[match.params.articleId]) // leaving out null checks for brevity/clarity in this example. ); -
Brendan Stromberger revised this gist
May 21, 2018 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,4 +1,4 @@ import * as selectors from '/selectors.js'; const selectorMiddleware = ({ getState }) => next => action => { const ret = next(action); -
Brendan Stromberger revised this gist
May 21, 2018 . 1 changed file with 11 additions and 6 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -8,23 +8,28 @@ export getProps = (_, props) => props; // me there's an explicit coupling introduced by using component props in a selector. export const getEntities => createSelector(getState, fp.get('entities')); export const getArticles => createSelector(getEntities, fp.get('articles')); export const getArticleIdFromProps => createSelector(getProps, fp.get('activeArticleId')); // ^ I also like to explicity indicate (in the selector name) when // a select is reliant on a prop in order to expose that coupling // whenever a prop-based selector is composed with another. export const getArticle => createSelector( getArticleIdFromProps, getArticles, fp.get // lodash/fp makes point-free function application particularly easy here. ); /* The above selectors then get exposed on window.selectors in debug mode. Prop-based selectors are ignored, thus window.selectors.getEntities & window.selectors.getArticles will be computed and exposed on window.selectors, but getArticle and getArticleIdFromProps will not. A potential solution that I often use is to store route params in Redux, so we could compute `activeArticle` using a selector provided by react-router-redux: */ export const getActiveArticle = createSelector( -
Brendan Stromberger revised this gist
May 21, 2018 . 1 changed file with 1 addition and 3 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -30,7 +30,5 @@ export const getArticle => createSelector( export const getActiveArticle = createSelector( getArticles, createMatchSelector({ path: '/articles/:articleId' }), // https://github.com/ReactTraining/react-router/blob/master/packages/react-router-redux/modules/selectors.js ((articles, match) => articles[match.params.articleId]) // assuming I don't need to do any null checks for this example… ); -
Brendan Stromberger revised this gist
May 21, 2018 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -4,7 +4,7 @@ This is a simple pattern I follow that helps me maintain an easily accessible gr computes selectors and sets the derived data on the window object, visible at `window.selectors` ### stateMiddleware.js simply exposes the redux state to the window object at `window.state`. nothing crazy here ### configureStore.js apply the two middlewares -
Brendan Stromberger revised this gist
May 21, 2018 . 1 changed file with 2 additions and 4 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -29,10 +29,8 @@ export const getArticle => createSelector( export const getActiveArticle = createSelector( getArticles, createMatchSelector({ path: '/articles/:articleId' }), // https://github.com/ReactTraining/react-router/blob/master/packages/react-router-redux/modules/selectors.js ((articles, match) => ({ activeArticleId: articles[match.params.articleId] // assuming I don't need to do any null checks for this example… }) ); -
Brendan Stromberger revised this gist
May 21, 2018 . 1 changed file with 11 additions and 11 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -25,14 +25,14 @@ export const getArticle => createSelector( A potential solution that I often use is to store route params in Redux, so we could compute `activeArticle` using a selector provided by react-router-redux: */ export const getActiveArticle = createSelector( getArticles, createMatchSelector({ path: '/articles/:articleId' }), // ^ https://github.com/ReactTraining/react-router/blob/master/packages/react-router-redux/modules/selectors.js ((articles, match) => ({ activeArticleId: articles[match.params.articleId] // ^ assuming I don't need to do any null checks for this example… }) ); -
Brendan Stromberger revised this gist
May 21, 2018 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -24,7 +24,7 @@ export const getArticle => createSelector( getArticleIdFromProps will not. A potential solution that I often use is to store route params in Redux, so we could compute `activeArticle` using a selector provided by react-router-redux: export const getActiveArticle = createSelector( getArticles, -
Brendan Stromberger revised this gist
May 21, 2018 . 1 changed file with 4 additions and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -20,8 +20,10 @@ export const getArticle => createSelector( /* These selectors then get exposed on window.selectors in debug mode. Prop-based selectors are ignored, thus window.selectors.getEntities & window.selectors.getArticles will be computed, but getArticle and getArticleIdFromProps will not. A potential solution that I often use is to store route params in Redux, so we could compute `activeArticle` using a selector written by react-router-redux: export const getActiveArticle = createSelector( -
Brendan Stromberger revised this gist
May 21, 2018 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -13,7 +13,7 @@ export const getArticleIdFromProps => createSelector(getProps, fp.get('activeArt // ^ I also like to explicity indicate (in the selector name) when // a select is reliant on a prop in order to expose that coupling. export const getArticle => createSelector( getArticleIdFromProps, getArticles, fp.get // lodash/fp makes point-free function application particularly easy here. ); -
Brendan Stromberger revised this gist
May 21, 2018 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,4 +1,4 @@ This is a simple pattern I follow that helps me maintain an easily accessible graph of all derived data flowing through a Redux application, and expose that derived data (as well as the Redux state itself) to the global window object in my browser's devtools. ### selectorMiddleware.js computes selectors and sets the derived data on the window object, visible at `window.selectors` -
Brendan Stromberger revised this gist
May 21, 2018 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,4 +1,4 @@ This is a simple pattern I follow that helps me maintain a graph of (potentially) all derived data flowing through a Redux application, and expose that derived data (as well as the Redux state itself) to the global window object in my browser's devtools. ### selectorMiddleware.js computes selectors and sets the derived data on the window object, visible at `window.selectors` -
Brendan Stromberger revised this gist
May 21, 2018 . 2 changed files with 1 addition and 24 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,24 +0,0 @@ This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -27,6 +27,7 @@ export const getArticle => createSelector( export const getActiveArticle = createSelector( getArticles, createMatchSelector({ path: '/articles/:articleId' }), // ^ https://github.com/ReactTraining/react-router/blob/master/packages/react-router-redux/modules/selectors.js ((articles, match) => ({ activeArticleId: articles[match.params.articleId] // ^ assuming I don't need to do any null checks for this example… -
Brendan Stromberger revised this gist
May 21, 2018 . 1 changed file with 24 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,24 @@ // this selector is referenced in selectors.js, I've copied and pasted it here verbatin from the react-router-redux repo. // https://github.com/ReactTraining/react-router/blob/master/packages/react-router-redux/modules/selectors.js import { matchPath } from "react-router"; export const getLocation = state => state.router.location; export const getAction = state => state.router.action; export const createMatchSelector = path => { let lastPathname = null; let lastMatch = null; return state => { const { pathname } = getLocation(state) || {}; if (pathname === lastPathname) { return lastMatch; } lastPathname = pathname; const match = matchPath(pathname, path); if (!match || !lastMatch || match.url !== lastMatch.url) { lastMatch = match; } return lastMatch; }; }; -
Brendan Stromberger revised this gist
May 21, 2018 . 1 changed file with 2 additions and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -13,8 +13,9 @@ export const getArticleIdFromProps => createSelector(getProps, fp.get('activeArt // ^ I also like to explicity indicate (in the selector name) when // a select is reliant on a prop in order to expose that coupling. export const getArticle => createSelector( getActiveArticleId, getArticles, fp.get // lodash/fp makes point-free function application particularly easy here. ); /* These selectors then get exposed on window.selectors in debug mode. -
Brendan Stromberger revised this gist
May 21, 2018 . No changes.There are no files selected for viewing
-
Brendan Stromberger revised this gist
May 21, 2018 . 1 changed file with 13 additions and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1 +1,13 @@ This is a simple pattern I follow that allows a me user to maintain a graph of (potentially) all derived data flowing through a Redux application, and expose that derived data (as well as the Redux state itself) to the global window object in my browser's devtools. ### selectorMiddleware.js computes selectors and sets the derived data on the window object, visible at `window.selectors` ### stateMiddleware.js simply exposes the redux state to the window object. nothing crazy here ### configureStore.js apply the two middlewares ### selectors.js an example of how I write selectors. -
Brendan Stromberger revised this gist
May 21, 2018 . 1 changed file with 0 additions and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -3,5 +3,4 @@ export default ({ getState }) => next => action => { if (process.env.NODE_ENV !== 'production') { window.state = getState(); } }; -
Brendan Stromberger revised this gist
May 21, 2018 . 3 changed files with 10 additions and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1 @@ This is a simple technique that allows a Redux user to maintain a graph of (potentially) all derived data flowing through a Redux application. This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,8 +1,9 @@ … import selectorMiddleware from './selectorMiddleware.js'; import stateMiddleware from './stateMiddleware.js'; … export default createStore( combineReducers({ … }), applyMiddleware(selectorMiddleware, stateMiddleware, …) ); This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,7 @@ export default ({ getState }) => next => action => { const ret = next(action); if (process.env.NODE_ENV !== 'production') { window.state = getState(); } return ret; }; -
Brendan Stromberger revised this gist
May 21, 2018 . 2 changed files with 34 additions and 3 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -2,16 +2,25 @@ import selectors as * from '/selectors.js'; const selectorMiddleware = ({ getState }) => next => action => { const ret = next(action); // Don't run this code in prod. if (process.env.NODE_ENV !== 'production') { const selectorKeys = Object.keys(selectors); window.selectors = {}; // Iterate through all the selectors and compute their state. for (let key of selectorKeys) { const selector = selectors[key]; if (!fp.isFunction(selector)) continue; const value = selector(getState()); // This is a dumb way of filtering out prop-based selectors // (we can't compute them as they are coupled to the lifecycle of a component instance). if (fp.isNil(value)) continue; window.selectors[key] = value; } } }; This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -9,4 +9,26 @@ export getProps = (_, props) => props; export const getEntities => createSelector(getState, fp.get('entities')); export const getArticles => createSelector(getEntities, fp.get('articles')); export const getArticleIdFromProps => createSelector(getProps, fp.get('activeArticleId')); // ^ I also like to explicity indicate (in the selector name) when // a select is reliant on a prop in order to expose that coupling. export const getArticle => createSelector( getArticles, getActiveArticleId ); /* These selectors then get exposed on window.selectors in debug mode. Prop-based selectors are ignored, thus window.selectors.getEntities & window.selectors.getArticles will be computed, but getArticle will not. A potential solution that i've used often is to store route params in Redux, so we could compute `activeArticle` using a selector written by react-router-redux: export const getActiveArticle = createSelector( getArticles, createMatchSelector({ path: '/articles/:articleId' }), ((articles, match) => ({ activeArticleId: articles[match.params.articleId] // ^ assuming I don't need to do any null checks for this example… }) ); */ -
Brendan Stromberger created this gist
May 20, 2018 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,8 @@ … import selectorMiddleware from './selectorMiddleware.js'; … export default createStore( combineReducers({ … }), applyMiddleware(selectorMiddleware, logger, …) ); This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,17 @@ import selectors as * from '/selectors.js'; const selectorMiddleware = ({ getState }) => next => action => { const ret = next(action); if (process.env.NODE_ENV !== 'production') { const selectorKeys = Object.keys(selectors); window.selectors = {}; for (let key of selectorKeys) { const selector = selectors[key]; if (!fp.isFunction(selector)) continue; const value = selector(getState()); if (fp.isNil(value)) continue; window.selectors[key] = value; } } return ret; }; This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,12 @@ import createSelector from 'reselect'; import fp from 'lodash/fp'; export getState = state => state; export getProps = (_, props) => props; // ^ just including this to mention I like to make prop access explicit, as it reminds // me there's an explicit coupling introduced by using component props in a selector. export const getEntities => createSelector(getState, fp.get('entities')); export const getArticles => createSelector(getEntities, fp.get('articles')); …