Redux: Another implementation for Selector Pattern










1














The following example shows the usual implementation for the selector pattern. Then I'll discuss the problems of this implementation. After that I'll suggest another implementation that may prove useful.



Usual implementation:



Here is how the root reducer looks like along with public selectors:



// reducers/index.js
import combineReducers from 'redux';
import * as Items from './items';
import * as Login from './login';

export default combineReducers(
login: Login.reducer,
items: Items.reducer
// more slices ...
);


// PUBLIC SELECTORS
export const getToken = state => Login.getToken(state.login);
export const getLoginError = state => Login.getError(state.login);
export const isLoginLoading = state => Login.isLoading(state.login);

export const getItems = state => Items.getToken(state.items);
export const getCurrentItem = state => Items.getError(state.items);
export const isItemsLoading = state => Items.isLoading(state.items);

// Selectors for data from other slices
// ...

// Selectors for derived data from multiple slices
export const getOwnedItems = (state) =>
// ...
;


Here is how a slice reducer look like along with its private selectors:



// reducers/login.js
import
// actions ...
from '../actions';

const defaultState =
// ...
;

export const reducer = (state = defaultState, action = ) =>
// Reducer ...
;


// PRIVATE SELECTORS
export const getToken = state => state.token;
export const getError = state => state.error;
export const isLoading = state => state.loading;


Another slice reducer and its private selectors:



// reducers/items.js
import
// actions ...
from '../actions';

const defaultState =
// ...
;

export const reducer = (state = defaultState, action = ) =>
// Reducer ...
;


// PRIVATE SELECTORS
export const getItems = state => state.items;
export const getCurrentItem = state => state.currentItem;
export const isItemsLoading = state => state.isLoading;


The usage of this implementation would be like this:



import getLoginError, isLoginLoading from '../reducers';

const mapStateToProps = state =>
return
error: getLoginError(state),
loading: isLoginLoading(state)
;
;


The Problem:



The previous implementation of selectors requires all public selectors to be defined in reducers/index.js. Notice that public selectors receives the full state which is managed by the root reducer defined in reducers/index.js.



Public selectors can be divided into two types. Extraction selectors just for extracting data from the state. And Derived information Selectors which computes derived information from the state. For the users all selectors are the same and their purpose is to separate the client code from the shape of the state.



First problem with the previous implementation is that all extraction selectors are written twice. Once as a public selector and another as a private selector where the public selector is calling the private one.



Second problem is that all private selectors for specific slice can only receive that slice but it is passed many times, once for each usage instance of a private selector for that slice which seems a good case for refactoring.



The following is another implementation for selectors that could prove useful:



Another implementation



The root reducer file will only provide one select() function which takes the full state and provide the public interface from which client code can retrieve data from the state.



The interface may consist of functions or collections of functions grouped under a name.
This structure allow us to provide an interface that will make implementing extraction selectors trivial in addition to the ability to provide more customized public selectors.



Please don't confuse the structure of the selector with the shape of the state. There is not coupling between the two. The public selector can still implement the same interface for the application even if the shape of the state changed.



Here is the public selectors implemented in select(state) function:



// reducers/index.js
import combineReducers from 'redux';
import * as Items from './items';
import * as Login from './login';

export default combineReducers(
login: Login.reducer,
items: Items.reducer
// more slices ...
);


// PUBLIC SELECTORS
export const select = (state) =>
return
login: Login.select(state.login),
items: Items.select(state.items),

// Selectors for drived data from multiple slices
getOwnedItems: () =>
// ...

;
;


Here is private selectors inplemented in the same way:



// reducers/login.js
import
// actions ...
from '../actions';

const defaultState =
// ...
;

export const reducer = (state = defaultState, action = ) =>
// Reducer ...
;


// PRIVATE SELECTORS
export const select = (state) =>
return
getToken: () => state.token,
getError: () => state.error,
isLoading: () => state.loading
;
;


Again for another slice:



// reducers/items.js
import
// actions ...
from '../actions';

const defaultState =
// ...
;

export const reducer = (state = defaultState, action = ) =>
// Reducer ...
;


// PRIVATE SELECTORS
export const select = (state) =>
return
getItems: () => state.items,
getCurrentItem: () => state.currentItem,
isLoading: () => state.loading
;
;


The usage of this implementation looks like this:



import select from '../reducers';

const mapStateToProps = state =>
return
error: select(state).login.getError(),
loading: select(state).login.isLoading()
;
;


Question 1:



What are the drawbacks of this implementation?



Question 2:



Is there another way to address the problems described above?



Thank you










share|improve this question


























    1














    The following example shows the usual implementation for the selector pattern. Then I'll discuss the problems of this implementation. After that I'll suggest another implementation that may prove useful.



    Usual implementation:



    Here is how the root reducer looks like along with public selectors:



    // reducers/index.js
    import combineReducers from 'redux';
    import * as Items from './items';
    import * as Login from './login';

    export default combineReducers(
    login: Login.reducer,
    items: Items.reducer
    // more slices ...
    );


    // PUBLIC SELECTORS
    export const getToken = state => Login.getToken(state.login);
    export const getLoginError = state => Login.getError(state.login);
    export const isLoginLoading = state => Login.isLoading(state.login);

    export const getItems = state => Items.getToken(state.items);
    export const getCurrentItem = state => Items.getError(state.items);
    export const isItemsLoading = state => Items.isLoading(state.items);

    // Selectors for data from other slices
    // ...

    // Selectors for derived data from multiple slices
    export const getOwnedItems = (state) =>
    // ...
    ;


    Here is how a slice reducer look like along with its private selectors:



    // reducers/login.js
    import
    // actions ...
    from '../actions';

    const defaultState =
    // ...
    ;

    export const reducer = (state = defaultState, action = ) =>
    // Reducer ...
    ;


    // PRIVATE SELECTORS
    export const getToken = state => state.token;
    export const getError = state => state.error;
    export const isLoading = state => state.loading;


    Another slice reducer and its private selectors:



    // reducers/items.js
    import
    // actions ...
    from '../actions';

    const defaultState =
    // ...
    ;

    export const reducer = (state = defaultState, action = ) =>
    // Reducer ...
    ;


    // PRIVATE SELECTORS
    export const getItems = state => state.items;
    export const getCurrentItem = state => state.currentItem;
    export const isItemsLoading = state => state.isLoading;


    The usage of this implementation would be like this:



    import getLoginError, isLoginLoading from '../reducers';

    const mapStateToProps = state =>
    return
    error: getLoginError(state),
    loading: isLoginLoading(state)
    ;
    ;


    The Problem:



    The previous implementation of selectors requires all public selectors to be defined in reducers/index.js. Notice that public selectors receives the full state which is managed by the root reducer defined in reducers/index.js.



    Public selectors can be divided into two types. Extraction selectors just for extracting data from the state. And Derived information Selectors which computes derived information from the state. For the users all selectors are the same and their purpose is to separate the client code from the shape of the state.



    First problem with the previous implementation is that all extraction selectors are written twice. Once as a public selector and another as a private selector where the public selector is calling the private one.



    Second problem is that all private selectors for specific slice can only receive that slice but it is passed many times, once for each usage instance of a private selector for that slice which seems a good case for refactoring.



    The following is another implementation for selectors that could prove useful:



    Another implementation



    The root reducer file will only provide one select() function which takes the full state and provide the public interface from which client code can retrieve data from the state.



    The interface may consist of functions or collections of functions grouped under a name.
    This structure allow us to provide an interface that will make implementing extraction selectors trivial in addition to the ability to provide more customized public selectors.



    Please don't confuse the structure of the selector with the shape of the state. There is not coupling between the two. The public selector can still implement the same interface for the application even if the shape of the state changed.



    Here is the public selectors implemented in select(state) function:



    // reducers/index.js
    import combineReducers from 'redux';
    import * as Items from './items';
    import * as Login from './login';

    export default combineReducers(
    login: Login.reducer,
    items: Items.reducer
    // more slices ...
    );


    // PUBLIC SELECTORS
    export const select = (state) =>
    return
    login: Login.select(state.login),
    items: Items.select(state.items),

    // Selectors for drived data from multiple slices
    getOwnedItems: () =>
    // ...

    ;
    ;


    Here is private selectors inplemented in the same way:



    // reducers/login.js
    import
    // actions ...
    from '../actions';

    const defaultState =
    // ...
    ;

    export const reducer = (state = defaultState, action = ) =>
    // Reducer ...
    ;


    // PRIVATE SELECTORS
    export const select = (state) =>
    return
    getToken: () => state.token,
    getError: () => state.error,
    isLoading: () => state.loading
    ;
    ;


    Again for another slice:



    // reducers/items.js
    import
    // actions ...
    from '../actions';

    const defaultState =
    // ...
    ;

    export const reducer = (state = defaultState, action = ) =>
    // Reducer ...
    ;


    // PRIVATE SELECTORS
    export const select = (state) =>
    return
    getItems: () => state.items,
    getCurrentItem: () => state.currentItem,
    isLoading: () => state.loading
    ;
    ;


    The usage of this implementation looks like this:



    import select from '../reducers';

    const mapStateToProps = state =>
    return
    error: select(state).login.getError(),
    loading: select(state).login.isLoading()
    ;
    ;


    Question 1:



    What are the drawbacks of this implementation?



    Question 2:



    Is there another way to address the problems described above?



    Thank you










    share|improve this question
























      1












      1








      1







      The following example shows the usual implementation for the selector pattern. Then I'll discuss the problems of this implementation. After that I'll suggest another implementation that may prove useful.



      Usual implementation:



      Here is how the root reducer looks like along with public selectors:



      // reducers/index.js
      import combineReducers from 'redux';
      import * as Items from './items';
      import * as Login from './login';

      export default combineReducers(
      login: Login.reducer,
      items: Items.reducer
      // more slices ...
      );


      // PUBLIC SELECTORS
      export const getToken = state => Login.getToken(state.login);
      export const getLoginError = state => Login.getError(state.login);
      export const isLoginLoading = state => Login.isLoading(state.login);

      export const getItems = state => Items.getToken(state.items);
      export const getCurrentItem = state => Items.getError(state.items);
      export const isItemsLoading = state => Items.isLoading(state.items);

      // Selectors for data from other slices
      // ...

      // Selectors for derived data from multiple slices
      export const getOwnedItems = (state) =>
      // ...
      ;


      Here is how a slice reducer look like along with its private selectors:



      // reducers/login.js
      import
      // actions ...
      from '../actions';

      const defaultState =
      // ...
      ;

      export const reducer = (state = defaultState, action = ) =>
      // Reducer ...
      ;


      // PRIVATE SELECTORS
      export const getToken = state => state.token;
      export const getError = state => state.error;
      export const isLoading = state => state.loading;


      Another slice reducer and its private selectors:



      // reducers/items.js
      import
      // actions ...
      from '../actions';

      const defaultState =
      // ...
      ;

      export const reducer = (state = defaultState, action = ) =>
      // Reducer ...
      ;


      // PRIVATE SELECTORS
      export const getItems = state => state.items;
      export const getCurrentItem = state => state.currentItem;
      export const isItemsLoading = state => state.isLoading;


      The usage of this implementation would be like this:



      import getLoginError, isLoginLoading from '../reducers';

      const mapStateToProps = state =>
      return
      error: getLoginError(state),
      loading: isLoginLoading(state)
      ;
      ;


      The Problem:



      The previous implementation of selectors requires all public selectors to be defined in reducers/index.js. Notice that public selectors receives the full state which is managed by the root reducer defined in reducers/index.js.



      Public selectors can be divided into two types. Extraction selectors just for extracting data from the state. And Derived information Selectors which computes derived information from the state. For the users all selectors are the same and their purpose is to separate the client code from the shape of the state.



      First problem with the previous implementation is that all extraction selectors are written twice. Once as a public selector and another as a private selector where the public selector is calling the private one.



      Second problem is that all private selectors for specific slice can only receive that slice but it is passed many times, once for each usage instance of a private selector for that slice which seems a good case for refactoring.



      The following is another implementation for selectors that could prove useful:



      Another implementation



      The root reducer file will only provide one select() function which takes the full state and provide the public interface from which client code can retrieve data from the state.



      The interface may consist of functions or collections of functions grouped under a name.
      This structure allow us to provide an interface that will make implementing extraction selectors trivial in addition to the ability to provide more customized public selectors.



      Please don't confuse the structure of the selector with the shape of the state. There is not coupling between the two. The public selector can still implement the same interface for the application even if the shape of the state changed.



      Here is the public selectors implemented in select(state) function:



      // reducers/index.js
      import combineReducers from 'redux';
      import * as Items from './items';
      import * as Login from './login';

      export default combineReducers(
      login: Login.reducer,
      items: Items.reducer
      // more slices ...
      );


      // PUBLIC SELECTORS
      export const select = (state) =>
      return
      login: Login.select(state.login),
      items: Items.select(state.items),

      // Selectors for drived data from multiple slices
      getOwnedItems: () =>
      // ...

      ;
      ;


      Here is private selectors inplemented in the same way:



      // reducers/login.js
      import
      // actions ...
      from '../actions';

      const defaultState =
      // ...
      ;

      export const reducer = (state = defaultState, action = ) =>
      // Reducer ...
      ;


      // PRIVATE SELECTORS
      export const select = (state) =>
      return
      getToken: () => state.token,
      getError: () => state.error,
      isLoading: () => state.loading
      ;
      ;


      Again for another slice:



      // reducers/items.js
      import
      // actions ...
      from '../actions';

      const defaultState =
      // ...
      ;

      export const reducer = (state = defaultState, action = ) =>
      // Reducer ...
      ;


      // PRIVATE SELECTORS
      export const select = (state) =>
      return
      getItems: () => state.items,
      getCurrentItem: () => state.currentItem,
      isLoading: () => state.loading
      ;
      ;


      The usage of this implementation looks like this:



      import select from '../reducers';

      const mapStateToProps = state =>
      return
      error: select(state).login.getError(),
      loading: select(state).login.isLoading()
      ;
      ;


      Question 1:



      What are the drawbacks of this implementation?



      Question 2:



      Is there another way to address the problems described above?



      Thank you










      share|improve this question













      The following example shows the usual implementation for the selector pattern. Then I'll discuss the problems of this implementation. After that I'll suggest another implementation that may prove useful.



      Usual implementation:



      Here is how the root reducer looks like along with public selectors:



      // reducers/index.js
      import combineReducers from 'redux';
      import * as Items from './items';
      import * as Login from './login';

      export default combineReducers(
      login: Login.reducer,
      items: Items.reducer
      // more slices ...
      );


      // PUBLIC SELECTORS
      export const getToken = state => Login.getToken(state.login);
      export const getLoginError = state => Login.getError(state.login);
      export const isLoginLoading = state => Login.isLoading(state.login);

      export const getItems = state => Items.getToken(state.items);
      export const getCurrentItem = state => Items.getError(state.items);
      export const isItemsLoading = state => Items.isLoading(state.items);

      // Selectors for data from other slices
      // ...

      // Selectors for derived data from multiple slices
      export const getOwnedItems = (state) =>
      // ...
      ;


      Here is how a slice reducer look like along with its private selectors:



      // reducers/login.js
      import
      // actions ...
      from '../actions';

      const defaultState =
      // ...
      ;

      export const reducer = (state = defaultState, action = ) =>
      // Reducer ...
      ;


      // PRIVATE SELECTORS
      export const getToken = state => state.token;
      export const getError = state => state.error;
      export const isLoading = state => state.loading;


      Another slice reducer and its private selectors:



      // reducers/items.js
      import
      // actions ...
      from '../actions';

      const defaultState =
      // ...
      ;

      export const reducer = (state = defaultState, action = ) =>
      // Reducer ...
      ;


      // PRIVATE SELECTORS
      export const getItems = state => state.items;
      export const getCurrentItem = state => state.currentItem;
      export const isItemsLoading = state => state.isLoading;


      The usage of this implementation would be like this:



      import getLoginError, isLoginLoading from '../reducers';

      const mapStateToProps = state =>
      return
      error: getLoginError(state),
      loading: isLoginLoading(state)
      ;
      ;


      The Problem:



      The previous implementation of selectors requires all public selectors to be defined in reducers/index.js. Notice that public selectors receives the full state which is managed by the root reducer defined in reducers/index.js.



      Public selectors can be divided into two types. Extraction selectors just for extracting data from the state. And Derived information Selectors which computes derived information from the state. For the users all selectors are the same and their purpose is to separate the client code from the shape of the state.



      First problem with the previous implementation is that all extraction selectors are written twice. Once as a public selector and another as a private selector where the public selector is calling the private one.



      Second problem is that all private selectors for specific slice can only receive that slice but it is passed many times, once for each usage instance of a private selector for that slice which seems a good case for refactoring.



      The following is another implementation for selectors that could prove useful:



      Another implementation



      The root reducer file will only provide one select() function which takes the full state and provide the public interface from which client code can retrieve data from the state.



      The interface may consist of functions or collections of functions grouped under a name.
      This structure allow us to provide an interface that will make implementing extraction selectors trivial in addition to the ability to provide more customized public selectors.



      Please don't confuse the structure of the selector with the shape of the state. There is not coupling between the two. The public selector can still implement the same interface for the application even if the shape of the state changed.



      Here is the public selectors implemented in select(state) function:



      // reducers/index.js
      import combineReducers from 'redux';
      import * as Items from './items';
      import * as Login from './login';

      export default combineReducers(
      login: Login.reducer,
      items: Items.reducer
      // more slices ...
      );


      // PUBLIC SELECTORS
      export const select = (state) =>
      return
      login: Login.select(state.login),
      items: Items.select(state.items),

      // Selectors for drived data from multiple slices
      getOwnedItems: () =>
      // ...

      ;
      ;


      Here is private selectors inplemented in the same way:



      // reducers/login.js
      import
      // actions ...
      from '../actions';

      const defaultState =
      // ...
      ;

      export const reducer = (state = defaultState, action = ) =>
      // Reducer ...
      ;


      // PRIVATE SELECTORS
      export const select = (state) =>
      return
      getToken: () => state.token,
      getError: () => state.error,
      isLoading: () => state.loading
      ;
      ;


      Again for another slice:



      // reducers/items.js
      import
      // actions ...
      from '../actions';

      const defaultState =
      // ...
      ;

      export const reducer = (state = defaultState, action = ) =>
      // Reducer ...
      ;


      // PRIVATE SELECTORS
      export const select = (state) =>
      return
      getItems: () => state.items,
      getCurrentItem: () => state.currentItem,
      isLoading: () => state.loading
      ;
      ;


      The usage of this implementation looks like this:



      import select from '../reducers';

      const mapStateToProps = state =>
      return
      error: select(state).login.getError(),
      loading: select(state).login.isLoading()
      ;
      ;


      Question 1:



      What are the drawbacks of this implementation?



      Question 2:



      Is there another way to address the problems described above?



      Thank you







      javascript redux react-redux






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked Nov 12 '18 at 15:46









      Meena Alfons

      5231626




      5231626



























          active

          oldest

          votes











          Your Answer






          StackExchange.ifUsing("editor", function ()
          StackExchange.using("externalEditor", function ()
          StackExchange.using("snippets", function ()
          StackExchange.snippets.init();
          );
          );
          , "code-snippets");

          StackExchange.ready(function()
          var channelOptions =
          tags: "".split(" "),
          id: "1"
          ;
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function()
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled)
          StackExchange.using("snippets", function()
          createEditor();
          );

          else
          createEditor();

          );

          function createEditor()
          StackExchange.prepareEditor(
          heartbeatType: 'answer',
          autoActivateHeartbeat: false,
          convertImagesToLinks: true,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: 10,
          bindNavPrevention: true,
          postfix: "",
          imageUploader:
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          ,
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          );



          );













          draft saved

          draft discarded


















          StackExchange.ready(
          function ()
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53265572%2fredux-another-implementation-for-selector-pattern%23new-answer', 'question_page');

          );

          Post as a guest















          Required, but never shown






























          active

          oldest

          votes













          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes















          draft saved

          draft discarded
















































          Thanks for contributing an answer to Stack Overflow!


          • Please be sure to answer the question. Provide details and share your research!

          But avoid


          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.

          To learn more, see our tips on writing great answers.





          Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


          Please pay close attention to the following guidance:


          • Please be sure to answer the question. Provide details and share your research!

          But avoid


          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.

          To learn more, see our tips on writing great answers.




          draft saved


          draft discarded














          StackExchange.ready(
          function ()
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53265572%2fredux-another-implementation-for-selector-pattern%23new-answer', 'question_page');

          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown







          這個網誌中的熱門文章

          How to read a connectionString WITH PROVIDER in .NET Core?

          Node.js Script on GitHub Pages or Amazon S3

          Museum of Modern and Contemporary Art of Trento and Rovereto