web-dev-qa-db-fra.com

Évitez de refaire le rendu d'une grande liste d'éléments avec react-redux

J'utilise redux avec react et TypeScript pour mon application. Je travaille avec de nombreux éléments utilisés à différents endroits de mon application. Mon état ressemble à ceci:

{
    items: {42: {}, 53: {}, ... }, //A large dictionary of items
    itemPage1: {
        itemsId: [ 42, 34, 4 ],
        ...
    },
    itemPage2: { ... 
    },
    ...
}

L'utilisateur peut modifier certains attributs de la variable items en envoyant certaines actions. Lorsque cela se produit, je dois redessiner les composants qui ont été modifiés dans chaque page. Le problème est que mes objets sont assez gros et que je ne peux pas me permettre de les redessiner à chaque petite modification. Je me demandais si cette approche fonctionnerait:

  • J'ai un premier composant <ItemPage1> qui se connecte au magasin pour obtenir tous les états stockés dans l'arborescence sous itemPage1 par ex. la liste des éléments id: itemsId.
  • Dans <ItemPage1>, je boucle sur la propriété itemsId pour générer plusieurs composants FilterItem: itemsId.map( itemId => return <FilterItem id=itemId>);
  • Enfin, chaque Item est connecté en utilisant ownProps pour obtenir la partie correcte de l'état:

    const mapStateToItemProps = (state, ownProps) => {
        return {
            item: state.items[ownProps.id],
        }
    }
    const mapDispatchToItemProps = (dispatch, ownProps) => {
        return null;
    }
    const FilterItem = connect(
        mapStateToItemProps,
        mapDispatchToItemProps
    )(Item)
    

Pouvez-vous confirmer ou réfuter que si je mets à jour l'élément de l'ID 42, seul cet élément sera restitué?

8
user3091275

Lorsque vous rendez une grande liste, vous devez prendre en compte peu de choses:

  • Réduisez le nombre total d'éléments DOM à rendre (en ne rendant pas les éléments non visibles à l'écran, également appelés virtualisation).
  • Ne pas restituer les éléments qui n'ont pas changé

En gros, ce que vous voulez éviter, c'est un re-rendu complet de votre liste (ou de votre page) lorsque l'utilisateur modifie une seule ligne. Ceci peut être réalisé exactement comme vous l'avez fait, c'est-à-dire: en transmettant au conteneur de liste uniquement les identifiants des éléments devant être restitués, et en mappant ces identifiants sur connect chaque composant à l'aide de ownProps. Si vous avez un composant dump <Item/>, votre composant <ItemPage/> créera un composant connecté connect(<Item/>).

Cela va fonctionner, si vous mettez une console.log('item rendered') dans votre classe de composants <Item/>, vous remarquerez qu'il n'y a qu'un seul appel.

MAIS(et c’est un gros mais), ce qui n’est pas évident quand on travaille avec react-redux est que tous les composants connectés qui dépendent de leur ownProps vont toujours render si n’importe quelle partie du changement d'état . Dans votre cas, même si les composants <Item/> ne seront pas restitués, leur composant enveloppé connect(Item) le sera! Si vous avez quelques dizaines d'éléments, vous pouvez rencontrer une certaine latence si les actions doivent être envoyées rapidement (par exemple lors de la saisie d'une entrée). Comment éviter ça? Utilisez une fonction d'usine pour utiliser ownProps comme premier accessoire:

const mapStateToItemProps = (_, initialProps) => (state) => {
    return {
        item: state.items[initialProps.id],  // we're not relying on the second parameters "ownProps" here, so the wrapper component will not rerender
    }
}
const mapDispatchToItemProps = (dispatch, ownProps) => {
    return null;
}
const FilterItem = connect(
    mapStateToItemProps,
    mapDispatchToItemProps
)(Item)

Je vous suggère de regarder cette autre réponse .

Vous pourriez également être intéressé par ces excellentes diapositives: Big List High Performance React & Redux

Et enfin, vous devriez absolument jeter un œil à react-virtualized pour effectuer la virtualisation de votre liste (c'est-à-dire afficher uniquement l'élément que l'utilisateur peut réellement voir).

9
Pierre Criulanscy

Ok, j'ai trouvé cette discussion: https://github.com/reactjs/redux/issues/1303

En bas, il est clairement indiqué (de plusieurs protagonistes):

[...] [...] react-redux s'occupe de cela. Il vous permet de spécifier des parties spécifiques de l'état qui vous tient à cœur et veille à ce que les composants React mis à jour ne soient plus mis à jour lorsque les parties pertinentes n'ont pas changé. _

[...] Je voulais juste comprendre parfaitement ce qui se passe sous le capot, donc si le magasin Redux est mis à jour mais qu'un état de composant spécifique n'a pas changé, Redux ne déclenchera pas la méthode forceUpdate () pour ce composant? [...]

Le composant wrapper généré par la fonction connect () de React-Redux effectue plusieurs vérifications pour tenter de réduire le nombre de fois que votre composant doit être restitué. Ceci inclut une implémentation par défaut de shouldComponentUpdate contrôle d'égalité des accessoires entrant dans votre composant (y compris ce qui est retourné par mapStateToProps). Donc, en règle générale, un composant connecté ne restitue que lorsque les valeurs extraites de l'état ont été modifiées.

Je pense donc que mon implémentation est bonne, elle ne rendra pas tous les éléments car un seul élément verra ses propriétés modifiées.

0
user3091275