Как уменьшить шаблонный код

Мы не будем здесь объяснять, как устроен redux и как он работает. Вы должны знать, что он использует действия и редукторы для управления состоянием, и большинство людей склонны использовать селекторы для чтения состояния из редукции.

вступление

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

Есть одно простое правило, которому я люблю следовать при работе над проектами react-redux. Если вы используете redux, используйте его везде! Не портите свой проект одними частями, которые используют состояние компонента, а другими - редукцией. Redux должен быть вашим «единственным источником истины». Не смешивайте это с setState. Единственные части / компоненты, которые могут использовать состояния компонентов, - это те, которые могут быть повторно использованы в проектах, которые не используют redux. Они не должны иметь никакого отношения к логике вашего приложения.

Проблема

Учитывая все это, я попал в странную ситуацию. Во всех своих проектах я использую библиотеку Material-UI. Он использует такие компоненты, как текстовые поля, переключатели, диалоги и т. Д., Которые получают свойства. Хорошим примером является Dialog. Он сам управляет своим стилем, если он открыт или закрыт, но мы решаем с помощью свойств, когда он должен быть открыт или закрыт. Поскольку состояние, открыто ли диалоговое окно или нет, связано с логикой моего приложения, им нужно было управлять с помощью redux.

Потому что я использую диалоговое окно на 90% для отображения предупреждения об удалении типа «Вы действительно хотите удалить этот элемент / компанию / статью и т. Д.?» писать действия и редукторы для каждого из этих удалений было бы просто безумием. Поэтому создание одного действия и редуктора для управления всеми состояниями диалогов было для меня правильным решением. Это был очень простой редуктор, который запоминает для каждого диалога, открыт он или нет.

Это весь код действия:

import * as types from './types';
export function setDialogIsOpen(id, isOpen){
  return {
    type: types.ON_DIALOG_OPEN_CHANGED,
    id,
    isOpen
  };
}

И это весь код редуктора:

import * as types from './types';
export default function dialogs(state={}, action){
switch (action.type) {
    case types.ON_DIALOG_OPEN_CHANGED:
    return {...state, [action.id]: action.isOpen};
    default:
    return state;
  }
}

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

Чтобы получить состояние isOpen для моего диалогового компонента, мне просто нужно было проверить это следующим образом:

<Dialog
   title={intl.formatMessage({id: 'delete_task_title'})}
   actions={actions}
   modal={false}
   open={dialogs.delete_task===true}
   onRequestClose={this.handleClose}>
   {intl.formatMessage({id: 'delete_task_message'})}
</Dialog>

Вы заметили, что весь чек равен dialogs.delete_task===true. Диалог будет открыт, только если delete_task был true. Если он был undefined или false, он был бы закрыт.

У нас есть простое решение для состояний диалогового окна, но что с другими «простыми» состояниями, такими как текущая задача, показать или скрыть div, идентификатор выбранной строки и т. Д. Все те случаи, когда вам нужно сохранить только очень простое значение.

Решение

Решение такое же, как и для диалогового окна, но переименовано;)

Действие:

import * as types from './types';
export function setSimpleValue(id, value){
  return {
    type: types.ON_SIMPLE_VALUE_CHANGED,
    id,
    value
  };
}

Редуктор:

import * as types from './types';
export default function simpleValues(state={}, action){
switch (action.type) {
    case types.ON_SIMPLE_VALUE_CHANGED:
    return {...state, [action.id]: action.value};
    default:
    return state;
  }
}

Он работает как диалоговое решение, но с переименованием, которое, как вы знаете, предназначено только для хранения простых значений. Его следует использовать только для «простых» значений! При хранении одиночных и простых значений, которые могут иметь только значение или нет, или могут быть просто trueили false. Как только это усложнится с массивами, несколькими значениями, вы должны сделать отдельное действие и редуктор.

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

Я бы рекомендовал использовать его на ранней стадии приложений, пока вы не на 100% уверены, какие действия и редукторы вам нужны. Очень удобно создавать простые прототипы без создания действий и редукторов.

Упорство

Если вы используете библиотеки сохраняемости, существует также «простое» решение. Просто создайте действие и редуктор, переименованные в persistentValues, и используйте тот же код с переименованием. Большинство библиотек сохраняемости позволяют вам определять белый / черный список для определения того, должны ли части вашего состояния сохраняться или нет. Просто поместите simpleValues в черный список или persistentValues в белый список.

Демо

Если вам интересно, как все это работает, вы можете взглянуть на это в демонстрационном приложении, которое использует response, redux и firebase: ReactMostWanted