web-dev-qa-db-fra.com

Réagissez: mettez à jour un élément de la liste sans recréer tous les éléments

Disons que j'ai une liste de 1000 articles. Et je l'ai rendu avec React, comme ceci:

class Parent extends React.Component {
  render() {
    // this.state.list is a list of 1000 items
    return <List list={this.state.list} />;
  }
}

class List extends React.Component {
  render() {
    // here we're looping through this.props.list and creating 1000 new Items
    var list = this.props.list.map(item => {
      return <Item key={item.key} item={item} />;
    });
    return <div>{list}</div>;
  }
}

class Item extends React.Component {
  shouldComponentUpdate() {
    // here I comparing old state/props with new
  }
  render() {
    // some rendering here...
  }
}

Avec une liste relativement longue, map () prend environ 10 à 20 ms et je peux remarquer un léger décalage dans l’interface. 

Puis-je empêcher la recréation de 1000 objets React chaque fois que je n'ai besoin que d'en mettre à jour un?

29
ch1p_

Vous pouvez le faire en utilisant n'importe quelle bibliothèque de gestion d'état, de sorte que votre Parent ne garde pas trace de this.state.list => votre List ne soit restitué que lorsque la nouvelle Item est ajoutée. Et l'individu Item sera rendu de nouveau lorsqu'il sera mis à jour.

Disons que vous utilisez redux.

Votre code deviendra quelque chose comme ceci:

// Parent.js
class Parent extends React.Component {
  render() {        
    return <List />;
  }
}

// List.js
class List extends React.Component {
  render() {        
    var list = this.props.list.map(item => {
      return <Item key={item.key} uniqueKey={item.key} />;
    });
    return <div>{list}</div>;
  }
}

const mapStateToProps = (state) => ({
  list: getList(state)
});

export default connect(mapStateToProps)(List);


// Item.js
class Item extends React.Component {
  shouldComponentUpdate() {
  }
  render() {
  }
}

const mapStateToProps = (state, ownProps) => ({
  item: getItemByKey(ownProps.uniqueKey)
});

export default connect(mapStateToProps)(Item);

Bien entendu, vous devez implémenter le réducteur et les deux sélecteurs getList et getItemByKey.

Avec cela, vous aurez Listre-rendu déclencheur si de nouveaux éléments sont ajoutés, ou si vous changez item.key (ce que vous ne devriez pas)

14
xiaofan2406

MODIFIER:

Mes suggestions initiales ne concernaient que les améliorations de l'efficacité des listes Rendues} et n'abordaient pas la question de la limitation du re-rendu. des composants à la suite du changement de liste.

Voir la réponse de @ xiaofan2406 pour une solution claire à la question initiale.


Les bibliothèques qui aident à rendre le rendu de longues listes plus efficace et plus facile}:

_ {React Infinite

React-Virtualized

9
Pineda

Lorsque vous modifiez vos données, l’opération par défaut est de restituer tous les composants enfants et de créer virtual dom pour déterminer le composant devant être rendu.

Donc, si nous pouvons laisser réagir, il ne reste plus qu’un élément à réémettre. Cela peut faire gagner du temps.

Vous pouvez utiliser shouldComponentsUpdate dans votre composant de liste.

Si, dans cette fonction, renvoie false, réagit ne créera pas de comportement virtuel à juger.

Je suppose que vos données sont comme ceci [{name: 'name_1'}, {name: 'name_2'}]

class Item extends React.Component {
  // you need judge if props and state have been changed, if not
  // execute return false;
  shouldComponentUpdate(nextProps, nextState) { 
    if (nextProps.name === this.props.name) return false;

    return true;
  }
  render() {
    return (
      <li>{this.props.name}</li>
   )
  }
}

En tant que réaction, restituez ce qui a été modifié comme composant. Donc, si vous ne modifiez que les données d'un élément, les autres ne seront pas rendus.

2
qiuyuntao

Il y a quelques choses que vous pouvez faire:

  1. Lorsque vous générez, assurez-vous de définir NODE_ENV en production. par exemple. NODE_ENV=production npm run build ou similaire. ReactJS effectue de nombreuses vérifications de sécurité lorsque NODE_ENV n'est pas défini en production, comme les vérifications PropType. Si vous les désactivez, vous obtiendrez une amélioration> 2x de la performance pour le rendu React. Elle est essentielle pour la construction de votre production (même si elle est désactivée pendant le développement - ces contrôles de sécurité aident à prévenir les bugs!). Vous constaterez peut-être que c'est suffisant pour le nombre d'éléments à prendre en charge.
  2. Si les éléments sont dans un panneau défilant et que vous ne pouvez en voir que quelques-uns, vous pouvez configurer les éléments uniquement pour rendre le sous-ensemble visible des éléments. Ceci est plus facile lorsque les articles ont une hauteur fixe. L’approche de base consiste à ajouter des accessoires firstRenderedlastRendered à votre état List (c’est bien le premier _/inclusive et le dernier exclusif). Dans List.render, restituez un filler vide div (ou tr si applicable) de la hauteur correcte (firstRendered * itemHeight), puis de votre plage d'éléments rendus [firstRendered, lastRendered), puis d'un autre div de remplissage avec la hauteur restante ((totalItems - lastRendered) * itemHeight). Assurez-vous de donner à vos fillers et à vos objets des clés fixes. Il vous suffit ensuite de gérer onScroll sur le div à défilement et de déterminer quelle est la plage correcte à restituer (en règle générale, vous souhaitez afficher un chevauchement décent en haut et en bas, vous devez également déclencher un setState pour modifier la plage lorsque vous vous approchez du bord). Une alternative plus folle consiste à rendre et à mettre en œuvre votre propre barre de défilement (ce qui correspond à la FixedDataTable de Facebook - https://facebook.github.io/fixed-data-table/ ). Il existe de nombreux exemples de cette approche générale ici https://react.rocks/tag/InfiniteScroll
  3. Utilisez une approche sideways loading à l'aide d'une bibliothèque de gestion d'état. Pour les grandes applications, c'est essentiel quand même. Plutôt que de passer de l'état supérieur aux éléments, demandez à chaque élément de récupérer son propre état, soit de l'état "global" (comme dans Flux classique), soit via le contexte React (comme dans les implémentations modernes de Flux, MobX, etc.). Ainsi, lorsqu'un élément change, seul cet élément doit être rendu de nouveau. 
1
TomW

Une façon d'éviter de parcourir la liste des composants lors de chaque rendu consiste à le faire en dehors de la fonction de rendu et de l'enregistrer dans une variable.

class Item extends React.Component {
    shouldComponentUpdate(nextProps, nextState) {
        return this.props.item != nextProps.item;
    }

    render() {
        return <li>{this.props.item}</li>;
    }
}

class List extends React.Component {
    constructor(props) {
        super(props);
        this.items = [];
        this.update = this.update.bind(this);
    }

    componentWillMount() {
        this.props.items.forEach((item, index) => { this.items[index] = <Item key={index} item={item} /> });
    }

    update(index) {

        this.items[index] = <Item key={index} item={'item changed'} />
        this.forceUpdate();
    }

    render() {
        return <div>
            <button onClick={() => { this.update(199); }}>Update</button>
            <ul>{this.items}</ul>
        </div>
    }
}
1
jpdelatorre