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?
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>;
}
Voici quelques cas où un composant React sera rendu de nouveau.
this.setState()
dans le composant. Ceci déclenchera les méthodes de cycle de vie des composants suivants: shouldComponentUpdate
> componentWillUpdate
> render
> componentDidUpdate
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)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.
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.
Il y a maintenant un crochet pour cela disponible sur npm:
https://www.npmjs.com/package/use-trace-update
(Divulgation, je l'ai publié)
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
Cela ressemblera à l'image ci-dessus avec la touche diff.
É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.