以下示例显示了选择器模式的通常实现。然后,我将讨论此实现的问题。之后,我将建议另一个可能有用的实现。
以下是根缩减器与公共选择器的外观:
// 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) => {
// ...
};
这是切片缩减器及其专用选择器的外观:
// 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;
另一个切片缩减器及其专用选择器:
// 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;
此实现的用法如下:
import { getLoginError, isLoginLoading } from '../reducers';
const mapStateToProps = state => {
return {
error: getLoginError(state),
loading: isLoginLoading(state)
};
};
选择器的先前实现要求在reducers/index.js
中定义所有公共选择器。请注意,公共选择器接收到完整状态,该状态由reducers/index.js
中定义的root reducer管理。
公共选择器可以分为两种类型。提取选择器仅用于从状态中提取数据。以及派生信息选择器,根据状态来计算派生信息。对于用户而言,所有选择器都是相同的,其目的是将客户端代码与状态的形状分开。
先前实现的第一个问题是所有提取选择器都写入了两次。曾经是公共选择器,另一个是私有选择器,其中公共选择器正在调用私有选择器。
第二个问题是,特定切片的所有私有选择器只能接收该切片,但是它被传递了很多次,对于该切片的私有选择器的每个使用实例,这一次似乎很合适重构。
以下是可能证明有用的选择器的另一种实现:
root reducer文件将仅提供一个select()
函数,该函数采用完整状态,并提供公共接口,客户端代码可以从该接口检索状态中的数据。
接口可以由按名称分组的功能或功能集合组成。 这种结构使我们能够提供一个接口,该接口除了提供更多定制的公共选择器的能力之外,还使实现提取选择器变得不容易。
请不要将选择器的结构与状态的形状混淆。两者之间没有耦合。即使状态的形状发生了变化,公共选择器仍可以为应用程序实现相同的接口。
以下是在select(state)
函数中实现的公共选择器:
// 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: () => {
// ...
}
};
};
以下是对私有选择器的补充方式:
// 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
};
};
再次进行切片:
// 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
};
};
此实现的用法如下:
import { select } from '../reducers';
const mapStateToProps = state => {
return {
error: select(state).login.getError(),
loading: select(state).login.isLoading()
};
};
此实现的缺点是什么?
还有另一种解决上述问题的方法吗?
谢谢