4
votes

Énorme objet dans un magasin Redux

Dans l'idéal, nous placerions dans Redux le résultat d'une requête de base de données qui pourrait être très, très volumineuse (100k + lignes).

Cela semble exagéré de mettre le résultat dans redux, existe-t-il un moyen propre de mettre une sorte de hachage ou d'horodatage dans le magasin de redux et de pouvoir obtenir les données réelles dans mon composant React?

En Java, nous pourrions en quelque sorte écraser les égaux et le hachage afin que cela ne fasse pas un égal profond, ce qui le rend beaucoup plus rapide.


11 commentaires

Pourquoi voudriez-vous qu'un composant React reçoive une telle quantité de données? Vous ne souhaitez pas effectuer des filtrages / transformations sur ces données avant qu'elles n'atteignent le composant / conteneur?


non, car nous voulons rendre les données dans un graphique ou une carte


Je suis désolé de ne pas comprendre votre question. Vous demandez-vous comment vérifier que les données du magasin sont les mêmes données sur le composant de réaction et qu'il n'y a eu aucun changement? La raison pour laquelle je suis confus est que la nature immuable de Redux résout ce problème. Il n'y a donc pas besoin d'un égal profond ou de quelque chose comme ça.


Disons que j'ai un gros objet, comparer un seul champ suffit pour savoir s'il est égal (c'est comme un horodatage). Je ne veux pas que redux recherche une différence dans l'arbre. La raison en est qu'il est possible que la différence soit très petite et qu'elle se situe profondément dans l'arbre des objets. Comment cette nature «inmutable» peut-elle résoudre ce problème? (Je ne parle pas du même objet)


Vous pouvez essayer un JWT. Par défaut, si quelque chose change, le jeton est invalidé. Je ne connais pas les implications en termes de performances, mais vous devriez peut-être vous pencher sur base64 ou JWT ou quelque chose de cette nature


@SeanKelly, nous allons éventuellement l'utiliser, en utilisant une sorte de jeton (nous pouvons générer des identifiants uniques). C'est un peu dommage qu'on ne puisse pas créer une sorte de méthode equals pour Redux qui écrase le comportement par défaut. Maintenant, la solution est d'avoir une partie de `` l'état '' en dehors de Redux ...


s'agit-il d'une structure complexe avec une imbrication profonde? ou allez-vous normaliser les données en collections plates? (si vous ne le faites pas - mieux vaut envisager de cette façon)


Je ne suis pas sûr de ce que vous recherchez ici ... Redux est simplement destiné à être un gestionnaire d'état. si vous avez besoin d'une clé unique ou d'une recherche basée sur une clé, vous pouvez stocker les données de manière hiérarchique (regrouper toutes les 100 000 lignes sous une seule clé) et utiliser une somme de contrôle comme clé (ou tout autre algorithme de hachage). c'est quelque chose que vous mettriez dans le réducteur. vous ne pouvez pas vraiment vous attendre à une comparaison approfondie sur un gros objet, cela n'a rien à voir avec le redux. Vous pouvez également utiliser les identifiants des enregistrements comme clé, par exemple en ayant quelque chose comme: {"1-100,000": data, "100,001-200,00": data2}


Avez-vous essayé un shouldComponentUpdate personnalisé dans le composant qui affiche ce gros objet? Vous pouvez y mettre l'horodatage ou la vérification du hachage


à partir de react native, vous pouvez attribuer une clé unique à l'élément react, comme l'id DOM, et vous pouvez obtenir avec AsyncStorage.getItem (clé), et pouvez la définir avec un hachage md5 avec un ensemble de données, et comparer avec créer un nouvel élément , mais vraiment je ne sais pas comment vous restituer ce gros fichier, et si vraiment nécessaire


Redux ne fait pas d’égaux profonds, ce qui, je pense, est votre principale préoccupation. Il ne devrait pas être problématique de stocker les données dans Redux si c'est ce dont vous avez besoin.


3 Réponses :


0
votes

Votre principale préoccupation semble être la performance, même si je suis d'accord avec les suggestions qui ont été mentionnées dans les commentaires, à savoir: utilisez une charge utile plus petite, vous devez rendre beaucoup de données dans un composant de réaction et vous en avez besoin pour être performant, pour y parvenir, vous devez utiliser un composant fenêtré, cela ne rendra que les lignes visibles et rendra tout le reste à la demande, rendant le rendu des ensembles de données vraiment longs vraiment performant, regardez dans https://bvaughn.github.io/react-virtualized/ et transmettez simplement les données au composant


1 commentaires

ne vous inquiétez pas pour les performances de rendu du widget, c'est une autre question ;-)



2
votes

Redux par défaut n'utilise pas de comparaison approfondie. https://redux.js.org/faq/ immutable-data # redux-shallow-checking-requires-immutability

Il y a pas mal d'informations sur cette page, mais un peu distillée - Redux vérifie uniquement si la référence de l'objet est modifiée.

Il est difficile de dire quel est le problème spécifique sans voir de code. Il est possible que vous placiez votre objet trop haut dans l'arbre et que Redux traverse les clés au premier niveau de votre énorme objet. Si tel est le cas, le simple fait d'encapsuler votre objet dans l'état Redux peut aider.

{
   huge: myHugeObject,
}

Mais en réalité, c'est plus un pansement. Si Redux traverse votre objet, vous ne l'utilisez probablement pas tout à fait correctement.

Un exemple de code aiderait vraiment à diagnostiquer le problème.


3 commentaires

Si c'est le même pointeur, c'est bien le même objet, pas vraiment le scénario. Que se passe-t-il quand il y a une différence et que celle-ci est profondément ancrée dans la structure de l'objet? Comment puis-je savoir comment redux «navigue» dans une structure d'objet pour vérifier une différence? Je sais que mon objet a un horodatage, si l'horodatage est différent les objets sont différents, pas besoin de vérifier autre chose


Redux ne saura pas si vous modifiez quelque chose de profond dans la structure de l'objet sans changer l'objet de niveau supérieur. Ceci est une source courante de bogues subtils lors de l'utilisation de Redux. Il est très courant de faire une copie des objets que vous mettez dans le magasin Redux, juste pour garantir qu'ils ne peuvent pas être modifiés par un autre code. Bien sûr, si vous avez un objet énorme, vous ne pourrez peut-être pas le faire - et vous devrez peut-être faire très attention à ne pas modifier l'objet en dehors du réducteur.


Je l'ai :-). Dans notre cas, Object aurait des pointeurs différents. C'est un 100% nouveau provenant de la base de données, mais avec une petite, très petite différence.



2
votes

Ce serait ma suggestion pour une performance ultime. Gardez à l'esprit que je ne l'ai pas testé moi-même, mais si je comprends bien votre problème, je ne vois aucune raison pour laquelle cela ne fonctionnerait pas.

Vous devez stocker 100 000 lignes de données et, idéalement, ces données doivent persister côté client. La meilleure solution ici est d'utiliser IndexedDB car vous pouvez effectuer des lectures et des écritures comme une base de données tout en ayant suffisamment de stockage. Comme le recommande Soleil, je vous suggère d'utiliser dexie pour interagir avec IndexedDB. Vous n'en avez pas besoin, mais vous devriez certainement l'avoir en raison de la flexibilité qu'il vous offrira.

Vous devez également être en mesure de modifier ces changements, et ce ne sera pas facile sans pousser le travail sur un autre fil. Donc, la suggestion est la suivante:

// all in dexie docs
this.props.db.animals.hook('updating', (modifications, key, obj, tran) => {
  if ("species" in modifications) {
    this.props.actions.task(modifications.species, key, obj, tran);
  }
})

Les données sont maintenant dans IndexedDB et vous pouvez l'interroger à tout moment. Vous avez maintenant besoin d'un moyen de pousser les pièces lourdes de performance sur un autre filetage. Heureusement, il y a redux-worker

  ...
  import { applyWorker } from 'redux-worker';
  // you can't have any external dependencies for your worker, so bundle them or find a way to inline them
  const worker = new Worker('./script.js') // alternatively inline an async function it with `greenlet-with-edge`
  const enhancer = compose(applyMiddleware(thunk, logger), applyWorker(worker));
  const store = createStore(rootReducer, {}, enhancer);

Mettez votre worker en vigueur

import reducer from './reducers';
import task from './task';
import { createWorker } from 'redux-worker';

let worker = createWorker();
worker.registerReducer(reducer);
worker.registerTask('DIFF_IT', function (a) {
  // do your diffing and expensive functions
  task(a);
});

Passez votre db dans votre composant et accrochez-vous à la mise à jour. Appliquez votre action

  async function seedData() {
    // serialize your data, hopefully have unqiue id field on each row
    const hundredK = toJSON(await fetch(source));
    // IndexedDB doesn't have tremendous write speed, so it may be better to chunk your rows into
    // 10k intervals for  seem to perform the best but vary based on PC / browser implementation
    const db = new Dexie("myDataSource");
    // set version of Indexed to use (1 is preferred for best compatibility)
    // and set the indexes
    db.version(1).stores({
      animals: 'id,type'
    });
    let mice = [];
    for (let i = 0; i < hundredK.length; i++) {
      mice.push({
        id: i,
        species: hundredK[i].species,
        type: hundredK[i][0].type,
        facts: {
          someText: "this could come from hundredK, but I'll hardcode it",
          someNumber: i
        }
      });
    }
  }


1 commentaires

Ce n'est pas une solution réelle pour nous, mais merci pour la réponse détaillée :-)