Lors de la récupération des données que j'obtiens: impossible d'effectuer une mise à jour de l'état React sur un composant non monté. L'application fonctionne toujours, mais React suggère que je pourrais causer une fuite de mémoire.
"Il s'agit d'un non-op, mais cela indique une fuite de mémoire dans votre application. Pour corriger, annulez tous les abonnements et les tâches asynchrones dans une fonction de nettoyage useEffect."
Pourquoi est-ce que je continue de recevoir cet avertissement?
J'ai essayé de rechercher ces solutions:
https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal
https://developer.mozilla.org/en-US/docs/Web/API/AbortController
mais cela me donnait toujours l'avertissement.
this.getArtistProfile = (id,includeGroups,market,limit,offset) => { return Promise.all([ this.getArtist(id), this.getArtistAlbums(id,includeGroups,market,limit,offset), this.getArtistTopTracks(id,market) ]) .then(response => { return ({ artist: response[0], artistAlbums: response[1], artistTopTracks: response[2] }) }) }
Éditer:
Dans mon fichier api, j'ai ajouté un AbortController()
et utilisé un signal
pour que je puisse annuler une demande.
export function spotifyAPI() { const controller = new AbortController() const signal = controller.signal // code ... this.getArtist = (id) => { return ( fetch( `https://api.spotify.com/v1/artists/${id}`, { headers: {"Authorization": "Bearer " + this.user_token} }, {signal}) .then(response => { return checkServerStat(response.status, response.json()) }) ) } // code ... // this is my cancel method this.cancelRequest = () => controller.abort() }
Mon spotify.getArtistProfile()
ressemble à ceci
const ArtistProfile = props => { const [artistData, setArtistData] = useState(null) const token = props.spotifyAPI.user_token const fetchData = () => { const id = window.location.pathname.split("/").pop() console.log(id) props.spotifyAPI.getArtistProfile(id, ["album"], "US", 10) .then(data => {setArtistData(data)}) } useEffect(() => { fetchData() return () => { props.spotifyAPI.cancelRequest() } }, []) return ( <ArtistProfileContainer> <AlbumContainer> {artistData ? artistData.artistAlbums.items.map(album => { return ( <AlbumTag image={album.images[0].url} name={album.name} artists={album.artists} key={album.id} /> ) }) : null} </AlbumContainer> </ArtistProfileContainer> ) }
mais parce que mon signal est utilisé pour les appels API individuels qui sont résolus dans un Promise.all
je ne peux pas abort()
cette promesse donc je Promise.all
toujours l'état.
3 Réponses :
Vous pouvez essayer de définir un état comme celui-ci et vérifier si votre composant est monté ou non. De cette façon, vous êtes sûr que si votre composant est démonté, vous n'essayez pas de récupérer quelque chose.
const [didMount, setDidMount] = useState(false); useEffect(() => { setDidMount(true); return () => setDidMount(false); }, []) if(!didMount) { return null; } return ( <ArtistProfileContainer> <AlbumContainer> {artistData ? artistData.artistAlbums.items.map(album => { return ( <AlbumTag image={album.images[0].url} name={album.name} artists={album.artists} key={album.id} /> ) }) : null} </AlbumContainer> </ArtistProfileContainer> )
J'espère que cela vous aidera.
didMount
sera true
à l'état non monté.
Pouvez-vous expliquer un peu plus pourquoi?
Le composant se monte, puis l'effet s'exécute et définit didMount
sur true
, puis le composant se démonte mais didMount
n'est jamais réinitialisé
Je pense que vous setDidMount(false)
dire setDidMount(false)
. Mais le problème serait que la fermeture du gestionnaire .then()
aura une référence à un didMount
antérieur
C'était une méthode pour résoudre un problème de SSR dans mon application, qui irait également avec ce cas. Sinon, la promesse devrait être annulée, je suppose.
Partager le AbortController
entre les requêtes fetch()
est la bonne approche.
Quand l' une des Promise
est annulée, Promise.all()
rejettera avec AbortError
:
<script src="//cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.development.js"></script> <script src="//cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.development.js"></script> <main></main>
function Component(props) { const [fetched, setFetched] = React.useState(false); React.useEffect(() => { const ac = new AbortController(); Promise.all([ fetch('http://placekitten.com/1000/1000', {signal: ac.signal}), fetch('http://placekitten.com/2000/2000', {signal: ac.signal}) ]).then(() => setFetched(true)) .catch(ex => console.error(ex)); return () => ac.abort(); // Abort both fetches on unmount }, []); return fetched; } const main = document.querySelector('main'); ReactDOM.render(React.createElement(Component), main); setTimeout(() => ReactDOM.unmountComponentAtNode(main), 1); // Unmount after 1ms
Pour moi, nettoyer l'état au démontage du composant aidé.
const [state, setState] = useState({}); useEffect(() => { myFunction(); return () => { setState({}); // This worked for me }; }, []); const myFunction = () => { setState({ name: 'Jhon', surname: 'Doe', }) }
L'avertissement est dû au fait que le
getArtistProfile()
PromisegetArtistProfile()
résout une fois le composant démonté. Soit annuler cette demande, ou si ce n'est pas possible ajouter un chèque dans le.then()
gestionnaire sisetArtistData()
n'est pas appelé si le composant a été démontéIl ne sera pas possible d'expliquer pourquoi cela se produit sans en savoir plus sur votre application en dehors de ce composant. Nous avons besoin de savoir ce qui provoque le montage / démontage de ce composant. Que se passe-t-il dans l'application lorsque vous obtenez l'erreur?
@ ııı Comment pourrais-je vérifier si le composant est démonté?