Redux: примеры - как реализовать пространство имен типа действия?

Созданный на 23 сент. 2015  ·  32Комментарии  ·  Источник: reduxjs/redux

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

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

Я что-то упустил? У вас, ребята, нет конфликтов именования типов действий?

Может ли кто-нибудь обновить примеры и показать мне, как вы справляетесь с этой проблемой, или указать мне правильное направление?

Мой конкретный случай связан с наличием 2 отдельных административных панелей на интерфейсе. Один для тестировщиков и один для клиентов, состояние которых будет храниться в отдельных разделах магазина ("testerAccount", "customerAccount"), но оба они смогут делать похожие вещи, например, ADD_VIDEO_UPLOAD, ADD_COMMENT и т. Д. И т. Д.

Я был бы очень признателен за вашу помощь здесь :)

question

Самый полезный комментарий

Строки по своей сути неплохи для размещения имен. URL-адреса представляют собой строки, и, похоже, они работают нормально.

Все 32 Комментарий

Привести accountType в действие? См. Также examples/real-world/reducers чтобы узнать, как можно написать фабрику редукторов и многократно использовать один и тот же код, создавая редукторы, реагирующие на разные действия.

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

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

Затем мы можем помочь вам найти способы упростить этот конкретный пример. Очень сложно предложить что-то конкретное в ответ на вопрос без кода.

@gaearon Я знаю, к клоните , но я не пытаюсь создавать многоразовые редукторы, создатели действий или СУХИЕ вещи, как раз наоборот.

Я пытаюсь изолировать группы действий друг от друга, чтобы два человека работали над двумя разными разделами системы и были уверены, что они не начнут случайно запускать редукторы друг друга из-за отсутствия пространства имен в именах типов действий.

Надеюсь, это проясняет ситуацию.

Я решил эту проблему в своем проекте (проектах), используя https://www.npmjs.com/package/flux-constant в сочетании с супер простой вспомогательной функцией под названием createActionTypes .

По сути, мой код таков:

// create-action-types.js
var fluxConstant = require('flux-constant');
module.exports = function (types) {
    return fluxConstant.set(types);
};

// in foo-action-types.js
module.exports = createAtionTypes([
    'ADD_FOO',
    'REMOVE_FOO'
]);

// in some-store.js
var fooActionTypes = require('foo-action-types');
function (state, action) {
    switch(action.type) {
        case fooActionTypes.ADD_FOO: 

        case fooActionTypes.REMOVE_FOO:
   }
}

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

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


let initialState = {
  "testerAccount": {
    "videoUploads": [],
    "messages": []
  },
  "customerAccount": {
    "videoUploads": [],
    "messages": []
  },
  "systemUserAccount": {
    "videoUploads": [],
    "messages": []
  }
};

Как вы избежите коллизии имен типов действий при реализации отдельных редукторов для добавления видео или сообщений для каждого из этих разделов?

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

Это сразу вызовет проблемы во время слияния кода, которые позже нужно будет решить с помощью хаков именования, например:

ADD_VIDEO_UPLOAD, ADD_TESTER_VIDEO_UPLOAD, ADD_VIDEO_UPLOAD_IN_SOME_SECTION и т. Д.

что снова является одной большой головной болью.

@koulmomo Это именно то, что я искал, спасибо :): +1: Просто и мощно.

@gaearon Я думаю, у нас должен быть хотя бы один пример, в котором используется либо этот пакет, либо новый пакет, который можно было бы назвать redux-constant?

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

Спасибо за вашу помощь в этом :)

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

Не думаю, что flux-constant - здесь отличное решение. Кажется, полагается на чеки instanceof . Это означает, что вы не можете сериализовать свои действия и позже воспроизвести их, потому что - бац! - их десериализованные типы не будут соответствовать сгенерированным.

Люди часто пытаются сделать Flux «проще», не осознавая, что нарушают его основные функции .

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

Я был введен в заблуждение кажущейся симметрией в вашем примере. Теперь я понимаю, что вы имели в виду, что здесь нет DRY, а симметрия в функциях очевидна только, как вы сказали в https://github.com/rackt/redux/issues/786#issuecomment -142649749.

Не думаю, что здесь нужна библиотека. Установите соглашение! Например, если ваши подпроекты или функции настолько разделены, возьмите за правило вызывать типы действий feature/ACTION_TYPE , например testers/UPLOAD_VIDEO или customers/UPLOAD_VIDEO . Это особенно приятно, если эти «группы» действительно соответствуют реальным папкам (или, лучше сказать, пакетам) на диске.

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

Я полностью за то, чтобы добавить пример huge-apps с разделением кода, типами действий с именами и т. Д. Это будет отдельная проблема.

Строки по своей сути неплохи для размещения имен. URL-адреса представляют собой строки, и, похоже, они работают нормально.

@gaearon Вы правы, я забыл о проблеме сериализации с решением на основе постоянной потока.

У меня нет проблем с пространством имен на основе строк, если мы можем легко различать модули / пространства имен в самом имени.

Решение на основе соглашения об именах - это то, что я видел раньше:

https://github.com/erikras/ducks-modular-redux

но я надеялся, что может быть лучший или «правильный» способ сделать это, основанный на вашем опыте.

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

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

Еще раз спасибо :)

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

Я бы посмотрел, чтобы эта «проблема» была объяснена более подробно в документации. У меня были / были такие же вопросы, как @pbc. Я не думаю: «Почему бы не сохранить все типы действий как константы в одном месте?» работает, если вы повторно используете несколько модулей в проектах и ​​рекомендацию «Установите соглашение!» звучит очень похоже на БЭМ в мире CSS. Однако мы переходим от БЭМ к модулям CSS, и я предполагаю, что что-то похожее на модули CSS, имена хэш-классов которых также необходимы для типов действий в больших проектах.

Насколько я могу судить, невозможно добиться как сериализации, так и отсутствия конфликтов.

Хорошее соглашение для многократно используемых модулей может использовать уже существующие «поставщики уникальности», такие как обратные доменные имена, или имя пользователя / репозиторий на github, или зарегистрированное имя модуля на npm.


РЕДАКТИРОВАТЬ: модули CSS делают это, определяя собственный язык поверх CSS и добавляя префиксы к именам классов во время предварительной обработки (в любом случае сводится к соглашению, но сгенерированному).

+1 за вопрос и обсуждение. Я также много боролся с этой проблемой как @pbc . Меня смущает то, что у нас есть combReducers для редукторов, что делает красивое дерево состояний, но действия, с другой стороны, более или менее похожи на глобальные. Судя по всему, любое пространство имен должно выполняться вручную.

Думаю, я нашел решение для сериализуемых строковых типов действий. Я думаю, что это одна из тех ситуаций, когда наш разум накладывает на что-то искусственные ограничения.

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

Пример здесь: https://gist.github.com/samsch/63a54e868d7fa2b6023a

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

Если, конечно, вы сможете доказать, что они уникальны.
Лучше всего использовать уже существующие поставщики уникальности: обратные доменные имена или имена модулей npm.

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

Допустим, у вас есть модуль «Геометрия» с типом действия «Площадь». Этот модуль используется в двух местах:

  1. Приложение-> Рисование-> Геометрия (namespace = "Drawing / Geometry / Area")
  2. ПриложениеB-> Тригонометрия-> Форма-> Геометрия (namespace = "Тригонометрия / Форма / Площадь")

Теперь у вас есть два конфликтующих пространства имен, в зависимости от того, где используется ваш модуль.

  • Не рекомендуется жестко задавать этот полный путь в вашем модуле Geometry для ActionType «Area».
  • Лучше пусть имя будет простым: «Площадь».
  • Подобно композиции редуктора, составьте пространство имен, добавив для каждого родительского элемента префикс.

Я экспериментирую со следующей схемой:

создайте каталог для каждого типа коллекции, который содержит:

  • consts
  • редуктор
  • составные части
  • саги

создать константы в следующем формате:

// song-store/song-store-consts.js
export const ADD = 'SONG_STORE.ADD'
export const REMOVE = 'SONG_STORE.REMOVE'

При использовании констант в редукторах, сагах или действиях import все они с * :

// song-store/song-store-actions.js
import * as SONG_STORE from './song-store-consts'

export function addSongStore(name) {
  return {
    type: SONG_STORE.ADD,
    name
  }
}

export function removeSongStore(songStoreId) {
  return {
    type: SONG_STORE.REMOVE,
    songStoreId
  }
}

К сожалению, не подходит для тряски деревьев. Было бы неплохо, если бы ES6 позволял:

import { ADD, REMOVE } as SONG_STORE from './song-store-actions'

Кто-нибудь знает, может ли Webpack 2 разумно встряхнуть дерево import * чтобы он не объединял код для экспорта, который не используется, даже если он импортирован с помощью * ?

Это кажется очень распространенной проблемой, и она часто возникает в нашем офисе:

  • Конфликты имен вызывают нежелательное поведение.
  • Жалобы на шаблон, связанный с созданием уникальных типов действий.

Мы попробовали несколько разных решений, но ничего не помогло:

  1. Хранение всех типов действий в виде констант в одном месте, безусловно, упрощает управление, но я обнаружил, что это становится немного громоздким, когда приложение растет.
  2. Случайно сгенерированные значения, упомянутые @samsch, действительно привлекли мое внимание, и это, безусловно, работает, но да, потеря части, удобочитаемой человеком, усложняет жизнь.
  3. Приведенный выше комментарий @philholden мне очень понравился, поскольку мы обычно используем архитектуру, управляемую функциями, а структура каталогов довольно описательна.

После экспериментов в течение последних нескольких месяцев мы решили использовать структуру каталогов для пространства имен наших типов действий. Печатать их вручную очень быстро надоедает, поэтому я попытался что-то сделать с помощью __filename но это не сработало из-за объединения кода и т. Д. Затем я сделал свой первый плагин Babel, который преобразует ключевое слово __filenamespace в статическую строку, которая, похоже, работает хорошо.

пример

В App/testerAccount/index.js :

// Something like this
const ADD_VIDEO_UPLOAD = `${__filenamespace}/ADD_VIDEO_UPLOAD`;
const ADD_COMMENT = `${__filenamespace}/ADD_COMMENT`;

// Will be transformed into something like this
const ADD_VIDEO_UPLOAD = 'App/testerAccount/ADD_VIDEO_UPLOAD';
const ADD_COMMENT = 'App/testerAccount/ADD_COMMENT';

Не стесняйтесь попробовать, я надеюсь, что это полезно. Было бы здорово получить обратную связь, особенно от @gaearon как создателя

https://www.npmjs.com/package/babel-plugin-filenamespace

Я действительно надеялся, что кто-нибудь предложит «стандарт». Для этого нам не нужны символы или объекты, нам просто нужно соглашение.

Мы будем делать "featureName$actionType" или "fileName/ACTION_TYPE" или "PROJECT.FEATURE.ACTION" ? Если мы все сможем в чем-то договориться, будет легче делиться редукторами.

Я думаю, что из-за нехватки других доступных соглашений Ducks стал стандартом де-факто.

const ACTION = 'app/feature/ACTION'; вполне достаточно.

@gaearon всегда упоминал, что соотношение 1: 1 между действиями и редукторами, вероятно, плохая идея, и я действительно согласен. Но как насчет случая, когда два разных представления действительно хотят вызвать один и тот же редуктор.
Например, Простой переключатель предпочтений можно включить или отключить в двух разных местах:
/ myaccount / toggleNotification
/ dashboard / toggleNotification
так что если у нас есть два редуктора, написанных с одинаковым содержанием в
редукторы / notifications.js
Копия: @ samit4me , @philholden


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

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

@ivks : здесь несколько мыслей.

Во-первых, вы можете отправить одно и то же действие из нескольких мест в приложении.

Во-вторых, у вас определенно может быть один и тот же редуктор, который будет прослушивать несколько типов действий и обрабатывать их одинаково.

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

@markerikson

Во-вторых, у вас определенно может быть один и тот же редуктор, который будет прослушивать несколько типов действий и обрабатывать их одинаково.

На самом деле я использовал redux-actions и не знал, что у него есть служебный метод combAction, который выполнит эту задачу. Просто нашел это, и в вашем приведенном выше заявлении упоминается то же самое (должно было быть более ясным, извините)
Большое спасибо за ответ.

Я не думаю, что Ducks правильно определяет в ActionTypes:

ДОЛЖНЫ иметь типы действий в форме npm-module-or-app / reducer / ACTION_TYPE

Причина в том, что одно действие может быть подписано более чем одним редуктором. Как сказал @mherodev , const ACTION = 'app/feature/ACTION'; имеет больше смысла, дополнительно я хотел бы добавить схему для действия: const ACTION = 'action://app/feature/ACTION'; .

Почему бы просто не использовать глобально инкрементное целое число для определения типов действий? например

let id = 0

function generateActionType (label /* for readability */) {
  id++
  return `app/feature/${id}/${label}`
}

Немного подумав об этом, чтобы убедиться, что мы не конфликтуем с другими разработчиками, мы пишем правило lint, которое смотрит в каталог (где у нас есть все редукторы, и каждый редуктор имеет свои собственные типы действий в качестве констант)

Мы планируем сделать линтинг таким образом, чтобы он сообщал о нарушении, если есть две константы с одинаковым значением.

Если есть предложения, дайте мне знать :)

Была ли эта страница полезной?
0 / 5 - 0 рейтинги