web-dev-qa-db-fra.com

Découvrez pourquoi un composant React est rendu à nouveau

Existe-t-il une approche systématique pour déboguer ce qui provoque la restitution d'un composant dans React? Je mets un simple console.log () pour voir le nombre de fois qu’il effectue le rendu, mais j’ai du mal à déterminer la cause du rendu du composant à plusieurs reprises i.e (4 fois) dans mon cas. Existe-t-il un outil permettant d'afficher une chronologie et/ou un ordre de tous les composants?

86
jasan

Si vous voulez un extrait court sans aucune dépendance externe, je trouve cela utile

componentDidUpdate(prevProps, prevState) {
  Object.entries(this.props).forEach(([key, val]) =>
    prevProps[key] !== val && console.log(`Prop '${key}' changed`)
  );
  Object.entries(this.state).forEach(([key, val]) =>
    prevState[key] !== val && console.log(`State '${key}' changed`)
  );
}

Voici un petit crochet que j'utilise pour suivre les mises à jour des composants de la fonction

function useTraceUpdate(props) {
  const prev = useRef(props);
  useEffect(() => {
    const changedProps = Object.entries(props).reduce((ps, [k, v]) => {
      if (prev.current[k] !== v) {
        ps[k] = [prev.current[k], v];
      }
      return ps;
    }, {});
    if (Object.keys(changedProps).length > 0) {
      console.log('Changed props:', changedProps);
    }
    prev.current = props;
  });
}

// Usage
function MyComponent(props) {
  useTraceUpdate(props);
  return <div>{props.children}</div>;
}
108
Jacob R

Voici quelques cas où un composant React sera rendu de nouveau.

  • Composant parent render
  • Appel de this.setState() dans le composant. Ceci déclenchera les méthodes de cycle de vie des composants suivants: shouldComponentUpdate> componentWillUpdate> render> componentDidUpdate
  • Changements dans la variable props du composant. Ceci déclenchera componentWillReceiveProps> shouldComponentUpdate> componentWillUpdate> render> componentDidUpdate (la méthode connect de react-redux déclenchera ceci lorsqu'il y a des modifications applicables dans le magasin Redux)
  • appelant this.forceUpdate qui est similaire à this.setState

Vous pouvez minimiser le rendu de votre composant en effectuant une vérification dans votre shouldComponentUpdate et en retournant false s'il n'en a pas besoin.

Une autre façon consiste à utiliser React.PureComponent ou des composants sans état. Les composants purs et sans état ne sont restitués qu'en cas de modification des accessoires.

56
jpdelatorre

La réponse de @ jpdelatorre est excellente pour mettre en évidence les raisons générales pour lesquelles un composant React peut être rendu de nouveau.

Je voulais juste plonger un peu plus profondément dans un exemple: lorsque les accessoires changent . La résolution du problème de la restitution du composant React est un problème courant et, selon mon expérience, bien souvent , il est nécessaire de déterminer quels accessoires changent .

Réagissez les composants lors du rendu chaque fois qu'ils reçoivent de nouveaux accessoires. Ils peuvent recevoir de nouveaux accessoires comme:

<MyComponent prop1={currentPosition} prop2={myVariable} />

ou si MyComponent est connecté à un magasin Redux:

function mapStateToProps (state) {
  return {
    prop3: state.data.get('savedName'),
    prop4: state.data.get('userCount')
  }
}

À tout moment, la valeur de prop1, prop2, prop3 ou prop4 change MyComponent sera restituée. Avec 4 accessoires, il n'est pas trop difficile de déterminer quels accessoires changent en plaçant un console.log(this.props) à ce début du bloc render. Cependant, avec des composants plus compliqués et de plus en plus d'accessoires, cette méthode est intenable.

Voici une approche utile (en utilisant lodash pour plus de commodité) afin de déterminer les modifications apportées à l'accessoire entraînant la restitution d'un composant:

componentWillReceiveProps (nextProps) {
  const changedProps = _.reduce(this.props, function (result, value, key) {
    return _.isEqual(value, nextProps[key])
      ? result
      : result.concat(key)
  }, [])
  console.log('changedProps: ', changedProps)
}

L'ajout de cet extrait de code à votre composant peut aider à révéler le coupable qui a provoqué des restitutions discutables, ce qui permet souvent de mieux comprendre les données inutiles acheminées dans les composants.

9
Cumulo Nimbus

Il y a maintenant un crochet pour cela disponible sur npm:

https://www.npmjs.com/package/use-trace-update

(Divulgation, je l'ai publié)

4
Damian Green

Les réponses ci-dessus sont très utiles, juste au cas où quelqu'un chercherait une méthode spécifique pour détecter la cause du re-rendu, alors j'ai trouvé cette librairie redux-logger très utile.

Ce que vous pouvez faire est d’ajouter la bibliothèque et permettre la différenciation entre les états (c’est là dans la documentation) comme:

const logger = createLogger({
    diff: true,
});

Et ajoutez le middleware dans le magasin.

Placez ensuite un console.log() dans la fonction de rendu du composant que vous souhaitez tester.

Ensuite, vous pouvez exécuter votre application et vérifier les journaux de la console. Chaque fois qu’il existe un journal, il vous indiquera la différence entre state (nextProps and this.props) et vous pourrez décider si le rendu est vraiment nécessaire enter image description here

Cela ressemblera à l'image ci-dessus avec la touche diff.

1
pritesh

Étrange personne n’a donné cette réponse mais je la trouve très utile, d’autant plus que les modifications des accessoires sont presque toujours profondément imbriquées.

Crochets fanboys:

import deep_diff from "deep-diff";
const withPropsChecker = WrappedComponent => {
  return props => {
    const prevProps = useRef(props);
    useEffect(() => {
      const diff = deep_diff.diff(prevProps.current, props);
      if (diff) {
        console.log(diff);
      }
      prevProps.current = props;
    });
    return <WrappedComponent {...props} />;
  };
};

"Vieux" fans de l'école:

import deep_diff from "deep-diff";
componentDidUpdate(prevProps, prevState) {
      const diff = deep_diff.diff(prevProps, this.props);
      if (diff) {
        console.log(diff);
      }
}

P.S. Je préfère toujours utiliser HOC (composant d'ordre supérieur) car parfois vous avez déstructuré vos accessoires au sommet et la solution de Jacob ne convient pas bien

Disclaimer: Aucune affiliation avec le propriétaire du package. Un simple clic sur des dizaines de fois pour essayer de repérer la différence entre des objets profondément imbriqués est une tâche ardue.

0
ZenVentzi