Redux: Another implementation for Selector Pattern
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
add a comment |
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
add a comment |
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
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
javascript redux react-redux
asked Nov 12 at 15:46
Meena Alfons
5231626
5231626
add a comment |
add a comment |
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
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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