Une fonction de rappel définit l'état du composant. Mais parfois l'abonnement qui fournit les données doit prendre fin. Étant donné que le rappel est exécuté de manière asynchrone, il ne sait pas si l'abonnement se termine juste après l'appel de service (qui exécute la fonction de rappel).
Ensuite, je vois l'erreur suivante dans la console:
Avertissement: impossible d'effectuer une mise à jour de l'état React sur un composant non monté. Il s'agit d'un no-op, mais cela indique une fuite de mémoire dans votre application. Pour résoudre ce problème, annulez tous les abonnements et les tâches asynchrones dans une fonction de nettoyage useEffect.
Existe-t-il un moyen d'accéder à l'état du composant, même si je suis dans la fonction de rappel?
Ce seraient les étapes:
5 Réponses :
Vous pouvez utiliser une référence comme celle-ci:
const mounted = useRef(false); useEffect(() => { mounted.current = true; return () => { mounted.current = false; }; }, []);
Ensuite, dans votre rappel, vous pouvez vérifier si mounted.current === false
et éviter de définir l'état
Voici un pseudo-code sur la façon dont vous pouvez utiliser useEffect pour voir si un composant est monté.
Il utilise useEffect pour écouter someService
lorsqu'il reçoit un message, il vérifie si le composant est monté (la fonction de nettoyage est également appelée lorsque le composant est démonté) et si c'est le cas, il utilise setServiceMessage
qui a été créé par useState pour définir les messages reçus par le service:
import { useState, useEffect } from 'react'; import someService from 'some-service'; export default props => { const userId = props.userId; const [serviceMessage, setServiceMessage] = useState([]); useEffect( () => { const mounted = { current: true }; someService.listen( //listen to messages for this user userId, //callback when message is received message => { //only set message when component is mounted if (mounted.current) { setServiceMessage(serviceMessage.concat(message)); } }); //returning cleanup function return () => { //do not listen to the service anymore someService.stopListen(userId); //set mounted to false if userId changed then mounted // will immediately be set to true again and someService // will listen to another user's messages but if the // component is unmounted then mounted.current will // continue to be false mounted.current = false; }; },//<-- the function passed to useEffects //the function passed to useEffect will be called //every time props.userId changes, you can pass multiple //values here like [userId,otherValue] and then the function //will be called whenever one of the values changes //note that when this is an object then {hi:1} is not {hi:1} //referential equality is checked so create this with memoization //if this is an object created by mapStateToProps or a function [userId] ); };
J'ai trouvé la réponse acceptée à cette question difficile à lire, et React fournit sa propre documentation sur cette question . Leur exemple est:
const Fade: React.FC<{}> = ({ children }) => { const [ className, setClassName ] = useState('fade') const [ newChildren, setNewChildren ] = useState(children) useEffect(() => { setClassName('fade') const timerId = setTimeout(() => { setClassName('fade show') setNewChildren(children) }, TIMEOUT_DURATION) return () => { clearTimeout(timerId) } }, [children]) return <Container fluid className={className + ' p-0'}>{newChildren}</Container> }
J'ai créé un composant que j'appelle <Fade>
qui fera apparaître / disparaître tous les enfants donnés. Notez qu'il repose sur les classes .fade
et .show
, bien que celles-ci puissent facilement être implémentées sans bootstrap.
import React, { useState, useEffect } from 'react'; function FriendStatus(props) { const [isOnline, setIsOnline] = useState(null); useEffect(() => { function handleStatusChange(status) { setIsOnline(status.isOnline); } ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange); // Specify how to clean up after this effect: return function cleanup() { ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange); }; }); if (isOnline === null) { return 'Loading...'; } return isOnline ? 'Online' : 'Offline'; }
Tout se résume à une règle: vous désabonner de vos tâches asynchrones dans la fonction de nettoyage renvoyée par useEffect
.
Vous pouvez renvoyer une fonction de useEffect , qui sera déclenchée lors du démontage d' un composant fonctionnel. Lis ça s'il te plait
import React, { useEffect } from 'react'; const ComponentExample = () => { useEffect(() => { // Anything in here is fired on component mount. return () => { // Anything in here is fired on component unmount. } }, []) }
Ce crochet (inspiré de la réponse de Mohamed) résout le problème d'une manière plus élégante:
function useMounted() { const mounted = useRef(true); useEffect(() => { return () => { mounted.current = false} }, [mounted]); return mounted; }
Je suppose que vous utilisez des hooks useState Vous pouvez utiliser useEffect (suggéré dans l'erreur) pour renvoyer une fonction de nettoyage pour muter une variable montée et vérifier cette variable avant d'appeler la fonction useState