107 votes

redux: état comme tableau d'objets vs objet indexé par id

Dans le chapitre sur la Conception de la Forme État, la documentation suggère de garder votre état d'un objet identifié par ID:

Garder chaque entité dans un objet stocké avec un ID comme clé, et d'utiliser les Id de référence à partir d'autres entités, ou des listes.

Ils vont à l'état

Pensez à l'application de l'état d'une base de données.

Je suis en train de travailler sur l'état de forme pour une liste de filtres, dont certaines seront ouvertes (elles sont affichées dans une fenêtre popup) ou des options choisies. Quand j'ai lu "Penser à l'application de l'état d'une base de données, j'ai pensé à les penser comme une réponse JSON comme il serait retourné à partir d'une API (lui-même soutenu par une base de données).

Donc, je pensais à elle comme

[{
    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',
  }]

Cependant, les docs proposer un format plus comme

{
   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',
  }
}

En théorie, il ne devrait pas d'importance aussi longtemps que les données sont sérialisables (sous la rubrique "État").

Je suis donc allé avec le tableau des objets de l'approche joyeusement, jusqu'à ce que j'ai écrit mon réducteur.

Avec l'objet-clé-en-id de l'approche (et l'utilisation libérale de la propagation de la syntaxe), l' OPEN_FILTER de la partie du réducteur devient

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

Alors que le tableau d'objets, c'est le plus détaillé (et l'aide de la fonction tributaires)

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));
    }
    ...

Donc mes questions sont de trois ordres:

1) c'Est la simplicité de l'réducteur de la motivation pour aller avec l'objet-clé-by-id approche? Existe-il d'autres avantages à cet état de forme?

et

2) Il me semble que l'objet de la carrosserie-by-id approche rend plus difficile à traiter avec la norme JSON in/out pour une API. (C'est pourquoi je suis allé avec le tableau d'objets en premier lieu.) Donc, si vous allez avec cette approche, ne vous utilisez simplement une fonction pour le transformer-et-vient entre le format JSON et de l'état de forme de format? Qui semble maladroit. (Mais si vous avez l'avocat qui approche, c'est une partie de votre raisonnement, que c'est moins maladroit que le tableau d'objets réducteur ci-dessus?)

et

3) je sais que Dan Abramov conçu redux à la théorie de l'état de la structure des données agnostique (comme l'a suggéré "Par convention, le haut niveau de l'état est un objet ou une autre touche de la valeur de la collection comme une Carte, mais techniquement, il peut être n'importe quel type," c'est moi qui souligne). Mais compte tenu de ce qui précède, c'est juste de l' "recommandée" pour garder un objet identifié par ID, ou il y en a d'autres imprévus des points de douleur, je vais courir à l'aide d'un tableau d'objets qui font que je doit tout abandonner et essayer de s'en tenir à un objet identifié par ID?

48voto

DDS Points 358

Q1: La simplicité du réducteur est un résultat de ne pas avoir à parcourir le tableau pour trouver la bonne entrée. Ne pas avoir à les rechercher dans le tableau est l'avantage. Les sélecteurs et les autres données accesseurs peuvent et le font souvent l'accès à ces articles par id. Avoir à parcourir le tableau pour chaque accès devient un problème de performance. Lors de vos tableaux sont de plus en plus, le problème s'aggrave brutalement. Aussi, comme votre application devient de plus en plus complexe, de montrer et de filtrage de données dans plus d'endroits, le problème s'aggrave. La combinaison peut être préjudiciable. En accédant les articles par id, le temps d'accès des changements d' O(n) de O(1), pour les grandes n (ici le tableau des éléments) qui fait une énorme différence.

Q2: Vous pouvez utiliser normalizr pour vous aider avec la conversion de l'API pour les stocker. Comme de normalizr V3.1.0 vous pouvez utiliser pour éliminer aller dans l'autre sens. Cela dit, les Applications sont plus souvent consommateurs que les producteurs de données et la conversion en magasin est généralement fait de plus en plus fréquemment.

Q3: Les problèmes que vous pourrez rencontrer en utilisant un tableau ne sont pas tellement des problèmes avec la capacité de stockage de la convention et/ou des incompatibilités, mais plus de problèmes de performances.

12voto

Marco Scabbiolo Points 3158

Pensez à l'application de l'état d'une base de données.

C'est l'idée clé.

1) Avoir des objets avec des Identifiants uniques permet de toujours utiliser l'identifiant lors de la référence à l'objet, de sorte que vous avez à passer le minimum de la quantité de données entre les actions et les réducteurs. Il est plus efficace que l'aide du tableau.trouver(...). Si vous utilisez le tableau de l'approche que vous avez à passer la totalité de l'objet et qui peut dégénérer très rapidement, vous risquez de vous retrouver à recréer l'objet sur différents réducteurs, des actions, ou même dans le conteneur (vous ne voulez pas que). Les vues seront toujours en mesure d'obtenir l'intégralité de l'objet, même si leurs associés réducteur ne contient l'ID, parce que lors de la cartographie de l'état, vous aurez la collection quelque part (la vue est l'ensemble de l'état de la carte pour les propriétés). À cause de tout ce que j'ai dit, les actions finissent par avoir le minimum de quantité de paramètres, et les réducteurs de la minime quantité d'informations, essayer, essayer les deux méthodes et vous verrez l'architecture finit plus évolutif et le nettoyer à l'aide IDs si les collections n'ont ID.

2) La connexion à l'API ne devrait pas affecter l'architecture de votre espace de stockage et les réducteurs, c'est pourquoi vous avez des actions, afin de maintenir la séparation des préoccupations. Il suffit de mettre votre logique de conversion dans et hors de l'API dans un module réutilisable, l'importation de ce module dans le cadre des actions que l'utilisation de l'API, et qui devrait être.

3) j'ai utilisé des tableaux de structures avec des IDs, et ce sont les conséquences imprévues que j'ai subi:

  • Recréer les objets en permanence toughout le code
  • En passant innecesary d'information pour les réducteurs et les actions
  • Comme consquence de cela, mauvais, pas propre et pas évolutif code.

J'ai fini par changer ma structure de données et la réécriture de code. Vous avez été averti, s'il vous plaît ne pas obtenir vous-même en difficulté.

Aussi:

4) la Plupart des collections avec les Id sont destinés à utiliser l'ID comme une référence à l'objet entier, vous devriez prendre avantage de cela. Les appels d'API permettra d'obtenir l'ID puis le reste des paramètres, il en va de vos actions et les réducteurs.

8voto

tobiasandersen Points 3883

1) c'Est la simplicité de l'réducteur de la motivation pour aller avec l'objet-clé-by-id approche? Existe-il d'autres avantages à cet état de forme?

La raison principale pour laquelle vous voulez garder garder entités dans les objets stockés avec des codes d'identification de touches (également appelé normalisé), c'est que c'est vraiment difficile à utiliser profondément imbriqués les objets (qui est ce que vous obtenez habituellement à partir des Api REST dans une application plus complexe) - à la fois pour vos pièces et de vos réducteurs.

C'est un peu dur pour illustrer les avantages d'un état normalisé avec votre exemple (que vous n'avez pas de profondément la structure imbriquée). Mais disons que les options (dans votre exemple) ont également eu un titre, et ont été créés par les utilisateurs de votre système. Que ferait la réponse ressembler à quelque chose comme ceci à la place:

[{
  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,
},
...
]

Maintenant, imaginons que vous vouliez créer un composant qui affiche une liste de tous les utilisateurs qui ont créé des options. Pour ce faire, vous aimerais tout d'abord avoir à demander tous les éléments, puis itérer sur chacun de leurs options, et enfin obtenir le created_by.nom d'utilisateur.

Une meilleure solution serait de normaliser la réponse dans:

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'
    }
  }
}

Avec cette structure, il est beaucoup plus facile et plus efficace, à la liste de tous les utilisateurs qui ont créé des options (nous les avons isolés dans des entités.optionCreators, donc nous avons juste à boucle au travers de cette liste).

Il est également très simple de montrer par exemple les noms de ceux qui ont créé des options pour l'élément de filtre avec l'ID 1:

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

2) Il me semble que l'objet de la carrosserie-by-id approche rend plus difficile la face à la norme JSON in/out pour une API. (C'est pourquoi je suis allé avec l' tableau d'objets en premier lieu.) Donc, si vous allez avec cette approche, faire il vous suffit d'utiliser une fonction pour le transformer-et-vient entre JSON format et de l'état de forme de format? Qui semble maladroit. (Si vous défenseur de cette approche, est la partie de votre raisonnement, que c'est moins maladroit que le tableau d'objets réducteur ci-dessus?)

Une réponse JSON peut être normalisée par exemple à l'aide normalizr.

3) je sais que Dan Abramov conçu redux théoriquement être l'état de la structure des données agnostique (comme l'a suggéré "Par convention, le de haut niveau de l'état est un objet ou une autre touche de la valeur de la collection comme un La carte, mais, techniquement, il peut être n'importe quel type," c'est moi qui souligne). Mais, compte tenu de ci-dessus, c'est "recommandée" pour garder un objet identifié par ID, ou il y en a d'autres imprévus des points de douleur, je vais exécuter par à l'aide d'un tableau d'objets qui font que je doit tout abandonner ce plan et essayer de s'en tenir à un objet identifié par ID?

C'est probablement une recommandation pour les plus complexes des applications avec beaucoup de profondément imbriqués API réponses. Dans votre exemple, il n'a pas vraiment beaucoup d'importance.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X