Réagir les méthodes du cycle de vie aux hooks
J'ai un blog simple avec des articles. Et je veux réécrire les classes en composants fonctionnels et en hooks. Maintenant, j'ai cette logique dans les méthodes de cycle de vie de ma page avec un formulaire d'édition / d'ajout: cela fonctionne bien.
componentDidUpdate(prevProps, prevState) {
if (this.props.match.params.id !== prevProps.match.params.id) {
if (prevProps.match.params.id) {
this.props.onUnload();
}
this.id = this.props.match.params.id;
if (this.id) {
return this.props.onLoad(userService.articles.get(this.id));
}
this.props.onLoad(null);
}
this.isLoading = false;
}
componentDidMount() {
if (this.id) {
this.isLoading = true;
return this.props.onLoad(userService.articles.get(this.id));
}
this.isLoading = false;
this.props.onLoad(null);
}
componentWillUnmount() {
this.props.onUnload();
}
shouldComponentUpdate(newProps, newState) {
if (this.props.match.params.id !== newProps.match.params.id) {
this.isLoading = true;
}
return true;
}
J'ai tout réécrit en crochets comme ça:
//componentDidMount
useEffect(() => {
if (id) {
setIsloading(true);
return props.onLoad(userService.articles.get(id));
}
setIsloading(false);
props.onLoad(null);
}, []);
useEffect(()=> {
prevId.current = id;
}, [id]
);
//componentDidUpdate
useEffect(() => {
//const id = props.match.params.id;
if (id !== prevId.current) {
if (prevId.current) {
props.onUnload();
}
if (id) {
return props.onLoad(userService.articles.get(id));
}
props.onLoad(null);
}
setIsloading(false);
});
//componentWillUnmount
useEffect(() => {
return props.onUnload();
}, []);
- J'ai eu une erreur - "Trop de rediffusions." à codesandbox code complet: codesandbox
C'est étrange, mais sur localhost, il n'y a pas d'erreur "Trop de rediffusions".
Je ne sais pas quoi faire avec ma méthode de classe "shouldComponentUpdate" comment la réécrire en hooks. J'ai essayé «mémo» mais je ne sais pas comment écrire dans ce cas.
Et de toute façon, il me manque quelque chose, car tout ne fonctionnera pas - il ne met pas à jour correctement les champs du formulaire.
Si vous avez une bonne connaissance des hooks de réaction, veuillez aider ou donner des conseils - comment y remédier?
Réponses
L'effet sans dépendance est à l'origine "Too many re-renders."
: il s'exécute après chaque rendu puis il appelle setIsLoading
à mettre à jour l'état ( loading
) qui provoquera le re-rendu du composant, qui exécutera le à effect
nouveau et le setState
sera appelé à nouveau effect
et ainsi de suite ...
//componentDidUpdate
useEffect(() => {
//const id = props.match.params.id;
if (id !== prevId.current) {
if (prevId.current) {
props.onUnload();
}
if (id) {
return props.onLoad(userService.articles.get(id));
}
props.onLoad(null);
}
setIsloading(false);
})
pour résoudre le problème, supprimez-le setIsLoading
ou ajoutez-le en IsLoading
tant que dépendance.
//componentDidUpdate
useEffect(() => {
...
setIsloading(false);
},[isLoading]);
vous pouvez également fusionner deux effets de montage en un seul comme celui-ci (le vôtre fonctionne également, mais je pense que c'est préférable d'un point de vue stylistique):
//componentDidMount
useEffect(() => {
if (id) {
setIsloading(true);
return props.onLoad(userService.articles.get(id));
}
setIsloading(false);
props.onLoad(null);
//componentWillUnmount
return function () {
props.onUnload()
}
}, []);
pour la deuxième puce: à propos de la réécriture de votre composant shouldComponentUpdate
; Je dois d'abord souligner que votre existence shouldComponentUpdate
n'est pas raisonnable, puisque vous revenez toujours true
et que vous ne l'utilisez que pour déclencher un loading
état; et il n'est pas éligible pour être écrit avec React.memo (qui est ~ l'équivalent des shouldComponentUpdate
composants en classe); vous n'avez donc besoin que de quelque chose à exécuter à chaque changement d'accessoires pour déterminer l' loading
état et pour cela, vous pouvez utiliser un effet avec props
comme dépendance comme ceci:
//manually keeping old props id
const prevPropsId = useRef();
useEffect(() => {
if (prevPropsId.current !== props.match.params.id) {
setIsLoading(true);
}
prevPropsId.current = props.match.params.id;
}, [props.match.params.id]);
// this hook only run if `props.match.params.id` change
sur la base de ma réalisation, cela fera le travail, (même si vous avez du mal à comprendre pourquoi vous écrivez la logique de cette façon en premier lieu) et si cela ne va pas bien, vous pouvez ajuster un peu la logique pour correspondre à vos besoins, vous obtenez le idée comment fonctionne l'effet de changement d'accessoires. vous devez également gérer typeError
au cas où id
, params
ou match
n'existerait pas et le chaînage facultatif peut être un outil pratique ici ->props.match?.params?.id
Si vous manquez les anciens hooks de cycle de vie, vous pouvez toujours les recréer en tant que hooks personnalisés:
function useComponentDidMount(effect) {
useEffect(() => {
effect();
}, []);
}
function useComponentWillUnmount(effect) {
useEffect(() => {
return effect;
}, []);
}
function useComponentDidUpdate(effect, dependencies) {
const hasMountedRef = useRef(false);
const prevDependenciesRef = useRef(null)
useEffect(() => {
// don't run on first render, only on subsequent changes
if (!hasMountedRef.current) {
hasMountedRef.current = true;
prevDependenciesRef.current = dependencies;
return;
}
// run effect with previous dependencies, like in componentDidUpdate!
effect(...prevDependenciesRef.current || []);
prevDependenciesRef.current = dependencies;
}, dependencies)
}
Utilisation (votre exemple remanié):
//componentDidMount
useComponentDidMount(() => {
if (id) {
setIsloading(true);
props.onLoad(userService.articles.get(id));
return;
}
setIsloading(false);
props.onLoad(null);
});
//componentDidUpdate
useComponentDidUpdate((prevId) => {
if (prevId) {
props.onUnload();
}
if (id) {
props.onLoad(userService.articles.get(id));
return;
}
props.onLoad(null);
setIsloading(false);
}, [id]);
//componentWillUnmount
useComponentWillUnmount(() => {
props.onUnload();
});