web-dev-qa-db-fra.com

Comment choisir la forme d'état Redux pour une application avec des vues de liste / détail et la pagination?

Imaginez que j'ai un certain nombre d'entrées (par exemple, des utilisateurs) dans ma base de données. J'ai également deux itinéraires, l'un pour la liste, l'autre pour les détails (où vous pouvez modifier l'entrée). Maintenant, j'ai du mal à aborder la structure des données.

Je pense à deux approches et à une combinaison des deux.

Ensemble de données partagé

  • Je navigue vers /list, Tous mes utilisateurs sont téléchargés depuis une API stockée dans le magasin redux, sous la clé users, j'ajoute également une sorte de users_offset Et users_limit Pour ne rendre qu'une partie de la liste
  • Je navigue ensuite vers /detail/<id> Et stocke currently_selected_user Avec <id> Comme val ... ce qui signifie que je pourrai obtenir les données de mon utilisateur avec quelque chose comme ceci users.find(res => res.id === currently_selected_user)
  • la mise à jour sera également agréable et facile, car je travaille avec un seul ensemble de données et des détails pointant dessus
  • l'ajout d'un nouvel utilisateur est également facile, encore une fois il suffit de travailler avec la même liste d'utilisateurs

Maintenant, le problème que j'ai avec cette approche est que, lorsque la liste des utilisateurs devient énorme (par exemple des millions), le téléchargement peut prendre un certain temps. Et aussi, quand je naviguerai directement vers /detail/<id>, Je n'aurai pas encore tous mes utilisateurs téléchargés, donc pour obtenir des données pour celle dont j'ai besoin, je vais d'abord devoir télécharger le tout. Des millions d'utilisateurs juste pour en éditer un.

Ensemble de données séparé

  • Je navigue vers /list, Et au lieu de télécharger tous mes utilisateurs depuis l'api, je n'en télécharge que quelques-uns, selon ce que mes users_per_page Et users_current_page Seraient définis sur , et je stockerais probablement les données sous users_currently_visible
  • Je navigue ensuite vers /detail/<id>, Stocke currently_selected_user Avec <id> Comme val ... et au lieu de chercher dans users_currently_visible Je télécharge simplement les données de l'utilisateur depuis l'api. .
  • lors de la mise à jour, je ne mettrai pas à jour users_currently_visible
  • ni ajouter

Ce que je considère comme un problème possible ici, c'est que je vais devoir, lors de la visite de /list, Télécharger à nouveau les données de l'API, car elles pourraient ne pas être synchronisées avec ce qui se trouve dans la base de données, je pourrais également télécharger inutilement les données des utilisateurs en détail, car elles pourraient déjà être accessoirement dans mon users_currently_visible

une sorte de manigances frankenstein-y

  • Je détaille, je fais la même chose que dans Ensemble de données séparées mais au lieu de télécharger directement les données de l'utilisateur depuis l'api, je vérifie d'abord:
    • ai-je un users_currently_visible
    • si oui, y a-t-il un utilisateur avec mon identifiant entre eux? si les deux sont vrais, je les utilise ensuite comme données utilisateur, sinon je fais l'appel api
  • même chose lors de la mise à jour, je vérifie si mon utilisateur existe entre users_currently_visible si c'est le cas, je mets aussi à jour cette liste, sinon je ne fais rien

Cela fonctionnerait probablement, mais n'a pas vraiment l'impression que c'est la bonne façon. J'aurais aussi probablement besoin de télécharger une nouvelle liste de users_currently_visible Lors de la visite de /list, Car j'aurais pu en ajouter une nouvelle ..

Y a-t-il une façon préférée de faire les fans? ... Je suis sûr que chaque utilisateur de redux doit avoir rencontré les mêmes choses.

Merci!

59
fxck

Veuillez consulter exemple "monde réel" du repo Redux.
Il montre exactement la solution à ce problème.

La forme de votre état devrait ressembler à ceci:

{
  entities: {
    users: {
      1: { id: 1, name: 'Dan' },
      42: { id: 42, name: 'Mary' }
    }
  },
  visibleUsers: {
    ids: [1, 42],
    isFetching: false,
    offset: 0
  }
}

Remarque Je stocke séparément entities (ID -> Object Objects) et visibleUsers (description des utilisateurs actuellement visibles avec l'état de pagination et les ID).

Cela semble similaire à votre approche "Ensemble de données partagées". Cependant, je ne pense pas que les inconvénients que vous citez soient de réels problèmes inhérents à cette approche. Jetons-y un œil.

Maintenant, le problème que j'ai avec cette approche est que lorsque la liste des utilisateurs devient énorme (disons des millions), le téléchargement peut prendre un certain temps

Vous n'avez pas besoin de les télécharger tous! La fusion de toutes les entités téléchargées vers entities ne signifie pas que vous devez toutes les interroger. entities doit contenir toutes les entités qui ont été téléchargées jusqu'à présent - pas toutes les entités dans le monde. Au lieu de cela, vous ne téléchargerez que ceux que vous affichez actuellement en fonction des informations de pagination.

lorsque je navigue directement vers/detail /, je n'ai pas encore téléchargé tous mes utilisateurs, donc pour obtenir des données pour un seul, je vais devoir les télécharger tous. Des millions d'utilisateurs juste pour en éditer un.

Non, vous n'en demanderiez qu'un. L'action de réponse se déclencherait et le réducteur responsable de entities fusionnerait cette entité unique dans l'état existant. Tout simplement parce que state.entities.users peut contenir plus d'un utilisateur ne signifie pas que vous devez tous les télécharger. Considérez entities comme un cache qui ne doit pas avoir être rempli.


Enfin, je vais vous rediriger vers le exemple "monde réel" du repo Redux. Il montre exactement comment écrire un réducteur pour les informations de pagination et le cache d'entité, et comment normaliser JSON dans vos réponses API avec normalizr afin qu'il soit facile pour les réducteurs d'extraire des informations des actions du serveur de manière uniforme.

75
Dan Abramov