Redux:选择器模式的另一种实现

时间:2018-11-12 15:46:18

标签: javascript redux react-redux

以下示例显示了选择器模式的通常实现。然后,我将讨论此实现的问题。之后,我将建议另一个可能有用的实现。

通常的实现方式:

以下是根缩减器与公共选择器的外观:

// 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()
  };
};

问题1:

此实现的缺点是什么?

问题2:

还有另一种解决上述问题的方法吗?

谢谢

0 个答案:

没有答案