Redux: еще одна реализация для Selector Pattern - PullRequest
0 голосов
/ 12 ноября 2018

В следующем примере показана обычная реализация для шаблона селектора.Тогда я буду обсуждать проблемы этой реализации.После этого я предложу другую реализацию, которая может оказаться полезной.

Обычная реализация:

Вот как выглядит корневой редуктор вместе с открытыми селекторами:

// 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.

Открытые селекторы можно разделить на два типа.Селекторы извлечения только для извлечения данных из состояния.И селекторы производной информации, которая вычисляет производную информацию от государства.Для пользователей все селекторы одинаковы, и их целью является отделение клиентского кода от формы состояния.

Первая проблема с предыдущей реализацией состоит в том, что все селекторы извлечения записываются дважды,Когда-то как открытый селектор, а другой как частный селектор, когда публичный селектор вызывает частный.

Вторая проблема заключается в том, что все частные селекторы для определенного слайса могут получать только этот слайс, но этопередается много раз, по одному разу для каждого экземпляра использования частного селектора для этого слайса, что кажется хорошим поводом для рефакторинга.

Ниже приведена другая реализация селекторов, которая может оказаться полезной:

Другойреализация

Корневой файл-редуктор будет содержать только одну функцию 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:

Есть ли другой способ решения проблем, описанных выше?

Спасибо

...