Состояние как массив объектов против объекта с ключом по идентификатору

javascript redux spread-syntax

23591 просмотра

3 ответа

3077 Репутация автора

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

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

Они продолжают заявлять

Думайте о состоянии приложения как о базе данных.

Я работаю над формой состояния для списка фильтров, некоторые из которых будут открыты (они отображаются во всплывающем окне) или имеют выбранные параметры. Когда я прочитал «Подумайте о состоянии приложения как о базе данных», я подумал о том, чтобы воспринимать их как ответ JSON, поскольку он будет возвращен из API (который сам поддерживается базой данных).

Так что я думал об этом как

[{
    id: '1',
    name: 'View',
    open: false,
    options: ['10', '11', '12', '13'],
    selectedOption: ['10'],
    parent: null,
  },
  {
    id: '10',
    name: 'Time & Fees',
    open: false,
    options: ['20', '21', '22', '23', '24'],
    selectedOption: null,
    parent: '1',
  }]

Тем не менее, документы предлагают формат больше похож на

{
   1: { 
    name: 'View',
    open: false,
    options: ['10', '11', '12', '13'],
    selectedOption: ['10'],
    parent: null,
  },
  10: {
    name: 'Time & Fees',
    open: false,
    options: ['20', '21', '22', '23', '24'],
    selectedOption: null,
    parent: '1',
  }
}

Теоретически, это не должно иметь значения, если данные сериализуются (под заголовком «Состояние») .

Так что я с удовольствием подходил к массиву объектов, пока не написал свой редуктор.

При подходе объект-ключ-по-идентификатору (и либеральном использовании синтаксиса расширения) OPEN_FILTERчасть редуктора становится

switch (action.type) {
  case OPEN_FILTER: {
    return { ...state, { ...state[action.id], open: true } }
  }

Принимая во внимание подход массива объектов, он более многословен (и зависит от вспомогательной функции)

switch (action.type) {
   case OPEN_FILTER: {
      // relies on getFilterById helper function
      const filter = getFilterById(state, action.id);
      const index = state.indexOf(filter);
      return state
        .slice(0, index)
        .concat([{ ...filter, open: true }])
        .concat(state.slice(index + 1));
    }
    ...

Итак, мои вопросы тройные:

1) Является ли простота редуктора мотивацией для использования подхода «объект-ключ-идентификатор»? Есть ли другие преимущества в этом состоянии?

а также

2) Кажется, что подход «объект-ключ-по-идентификатору» усложняет работу со стандартным входом / выходом JSON для API. (Вот почему я пошел с массивом объектов в первую очередь.) Так что, если вы пойдете с этим подходом, вы просто используете функцию для преобразования его назад и вперед между форматом JSON и форматом формы состояния? Это кажется неуклюжим. (Хотя, если вы отстаиваете такой подход, вы считаете, что это менее неуклюже, чем приведенный выше редуктор массива объектов?)

а также

3) Я знаю, что Дэн Абрамов разработал редукс, чтобы теоретически быть независимым от структуры данных состояния (как предполагает «По соглашению, состоянием верхнего уровня является объект или какой-либо другой набор значений ключа, например Map, но технически это может быть любой типа " акцент мой"). Но, учитывая вышесказанное, это просто «рекомендуется» сохранить объект с ключом ID или есть другие непредвиденные болевые точки, с которыми я столкнусь, используя массив объектов, которые делают его таким, что я должен просто прекратить это планировать и пытаться придерживаться объекта с ключом по идентификатору?

Автор: nickcoxdotme Источник Размещён: 18.07.2016 07:57

Ответы (3)


11 плюса

4067 Репутация автора

Думайте о состоянии приложения как о базе данных.

Это ключевая идея.

1) Наличие объектов с уникальными идентификаторами позволяет всегда использовать этот идентификатор при обращении к объекту, поэтому вы должны передавать минимальное количество данных между действиями и редукторами. Это более эффективно, чем использование array.find (...). Если вы используете подход массива, вы должны передать весь объект, и это может очень быстро запутаться, вы можете в конечном итоге воссоздать объект на разных редукторах, действиях или даже в контейнере (вы этого не хотите). Представления всегда смогут получить полный объект, даже если связанный с ним редуктор содержит только идентификатор, потому что при отображении состояния вы получите коллекцию где-то (представление получает все состояние, чтобы сопоставить его со свойствами). Из-за всего, что я сказал, действия в конечном итоге имеют минимальное количество параметров и уменьшают минимальное количество информации, попробуйте,

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

3) Я использовал массивы для структур с идентификаторами, и это непредвиденные последствия, которые я перенес:

  • Воссоздание объектов постоянно в коде
  • Передача необходимой информации редукторам и действиям
  • Как следствие, плохой, не чистый и не масштабируемый код.

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

Также:

4) Большинство коллекций с идентификаторами предназначены для использования идентификатора в качестве ссылки на весь объект, вы должны воспользоваться этим. Вызовы API получат идентификатор, а затем остальные параметры, равно как и ваши действия и редукторы.

Автор: Marco Scabbiolo Размещён: 18.07.2016 08:18

42 плюса

3615 Репутация автора

Решение

Q1: Простота редуктора является результатом того, что нет необходимости искать в массиве, чтобы найти правильную запись. Отсутствие необходимости поиска в массиве является преимуществом. Селекторы и другие средства доступа к данным могут и часто получают доступ к этим элементам id. Необходимость поиска в массиве для каждого доступа становится проблемой производительности. Когда ваши массивы становятся больше, проблема производительности резко ухудшается. Кроме того, по мере того как ваше приложение становится более сложным, показывая и фильтруя данные в большем количестве мест, проблема также ухудшается. Комбинация может быть вредной. При доступе к элементам idвремя доступа изменяется с O(n)на O(1), что для больших n(здесь элементов массива) имеет огромное значение.

Q2: Вы можете использовать, normalizrчтобы помочь вам с преобразованием из API для хранения. Начиная с normalizr V3.1.0, вы можете использовать денормализацию, чтобы пойти другим путем. Тем не менее, приложения часто являются скорее потребителями, чем производителями данных, и поэтому преобразование в хранилище обычно выполняется чаще.

Вопрос 3. Проблемы, с которыми вы столкнетесь при использовании массива, - это не столько проблемы с соглашением о хранилище и / или несовместимостью, сколько проблемы с производительностью.

Автор: DDS Размещён: 18.07.2016 08:27

8 плюса

5076 Репутация автора

1) Является ли простота редуктора мотивацией для использования подхода «объект-ключ-идентификатор»? Есть ли другие преимущества в этом состоянии?

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

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

[{
  id: 1,
  name: 'View',
  open: false,
  options: [
    {
      id: 10, 
      title: 'Option 10',
      created_by: { 
        id: 1, 
        username: 'thierry' 
      }
    },
    {
      id: 11, 
      title: 'Option 11',
      created_by: { 
        id: 2, 
        username: 'dennis'
      }
    },
    ...
  ],
  selectedOption: ['10'],
  parent: null,
},
...
]

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

Лучшим решением было бы нормализовать ответ на:

results: [1],
entities: {
  filterItems: {
    1: {
      id: 1,
      name: 'View',
      open: false,
      options: [10, 11],
      selectedOption: [10],
      parent: null
    }
  },
  options: {
    10: {
      id: 10,
      title: 'Option 10',
      created_by: 1
    },
    11: {
      id: 11,
      title: 'Option 11',
      created_by: 2
    }
  },
  optionCreators: {
    1: {
      id: 1,
      username: 'thierry',
    },
    2: {
      id: 2,
      username: 'dennis'
    }
  }
}

С этой структурой намного проще и эффективнее составить список всех пользователей, которые создали параметры (у нас они изолированы в entity.optionCreators, поэтому нам просто нужно пройтись по этому списку).

Также довольно просто показать, например, имена пользователей тех, кто создал опции для элемента фильтра с идентификатором 1:

entities
  .filterItems[1].options
  .map(id => entities.options[id])
  .map(option => entities.optionCreators[option.created_by].username)

2) Кажется, что подход «объект-ключ-по-идентификатору» усложняет работу со стандартным входом / выходом JSON для API. (Вот почему я пошел с массивом объектов в первую очередь.) Так что, если вы пойдете с этим подходом, вы просто используете функцию для преобразования его назад и вперед между форматом JSON и форматом формы состояния? Это кажется неуклюжим. (Хотя, если вы отстаиваете такой подход, вы считаете, что это менее неуклюже, чем приведенный выше редуктор массива объектов?)

JSON-ответ может быть нормализован с использованием, например, normalizr .

3) Я знаю, что Дэн Абрамов разработал редукс, чтобы теоретически быть независимым от структуры данных состояния (как предполагает «По соглашению, состоянием верхнего уровня является объект или какой-либо другой набор значений ключей, таких как Map, но технически это может быть любой типа "акцент мой"). Но, учитывая вышесказанное, это просто «рекомендуется» сохранить объект с ключом ID или есть другие непредвиденные болевые точки, с которыми я столкнусь, используя массив объектов, которые делают его таким, что я должен просто прекратить это планировать и пытаться придерживаться объекта с ключом по идентификатору?

Это, вероятно, рекомендация для более сложных приложений с множеством глубоко вложенных ответов API. Однако в вашем конкретном примере это не так уж важно.

Автор: tobiasandersen Размещён: 18.07.2016 10:16
Вопросы из категории :
32x32