Évidemment, je ne nettoie pas correctement et j'annule la requête GET axios comme je devrais être. Sur mon local, je reçois un avertissement qui dit
Impossible d'effectuer une mise à jour d'état React sur un composant non monté. Il s'agit d'un no-op, mais il indique une fuite de mémoire dans votre application. Pour corriger, annulez tous les abonnements et tâches asynchrones dans un Fonction de nettoyage useEffect.
Sur stackblitz, mon code fonctionne, mais pour une raison quelconque, je ne peux pas cliquer sur le bouton pour afficher l'erreur. Il montre toujours toujours les données retournées.
https://codesandbox.io/s/8x5lzjmwl8
Veuillez consulter mon code et trouver mon défaut.
useAxiosFetch.js
import {useState, useEffect} from 'react'
import axios from 'axios'
const useAxiosFetch = url => {
const [data, setData] = useState(null)
const [error, setError] = useState(null)
const [loading, setLoading] = useState(true)
let source = axios.CancelToken.source()
useEffect(() => {
try {
setLoading(true)
const promise = axios
.get(url, {
cancelToken: source.token,
})
.catch(function (thrown) {
if (axios.isCancel(thrown)) {
console.log(`request cancelled:${thrown.message}`)
} else {
console.log('another error happened')
}
})
.then(a => {
setData(a)
setLoading(false)
})
} catch (e) {
setData(null)
setError(e)
}
if (source) {
console.log('source defined')
} else {
console.log('source NOT defined')
}
return function () {
console.log('cleanup of useAxiosFetch called')
if (source) {
console.log('source in cleanup exists')
} else {
source.log('source in cleanup DOES NOT exist')
}
source.cancel('Cancelling in cleanup')
}
}, [])
return {data, loading, error}
}
export default useAxiosFetch
index.js
import React from 'react';
import useAxiosFetch from './useAxiosFetch1';
const index = () => {
const url = "http://www.fakeresponse.com/api/?sleep=5&data={%22Hello%22:%22World%22}";
const {data,loading} = useAxiosFetch(url);
if (loading) {
return (
<div>Loading...<br/>
<button onClick={() => {
window.location = "/awayfrom here";
}} >switch away</button>
</div>
);
} else {
return <div>{JSON.stringify(data)}xx</div>
}
};
export default index;
Voici le code final avec tout fonctionnant au cas où quelqu'un d'autre reviendrait.
import {useState, useEffect} from "react";
import axios, {AxiosResponse} from "axios";
const useAxiosFetch = (url: string, timeout?: number) => {
const [data, setData] = useState<AxiosResponse | null>(null);
const [error, setError] = useState(false);
const [errorMessage, setErrorMessage] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
let unmounted = false;
let source = axios.CancelToken.source();
axios.get(url, {
cancelToken: source.token,
timeout: timeout
})
.then(a => {
if (!unmounted) {
// @ts-ignore
setData(a.data);
setLoading(false);
}
}).catch(function (e) {
if (!unmounted) {
setError(true);
setErrorMessage(e.message);
setLoading(false);
if (axios.isCancel(e)) {
console.log(`request cancelled:${e.message}`);
} else {
console.log("another error happened:" + e.message);
}
}
});
return function () {
unmounted = true;
source.cancel("Cancelling in cleanup");
};
}, []);
return {data, loading, error, errorMessage};
};
export default useAxiosFetch;
Le problème dans votre cas est que sur un réseau rapide, les demandes entraînent une réponse rapide et ne vous permettent pas de cliquer sur le bouton. Sur un réseau étranglé que vous pouvez réaliser via ChromeDevTools, vous pouvez visualiser correctement ce comportement
Deuxièmement, lorsque vous essayez de vous éloigner en utilisant window.location.href = 'away link'
react n'a pas de changement pour déclencher/exécuter le nettoyage du composant et donc la fonction de nettoyage de useEffect
ne sera pas déclenchée.
Utilisation du routeur fonctionne
import React from 'react'
import ReactDOM from 'react-dom'
import {BrowserRouter as Router, Switch, Route} from 'react-router-dom'
import useAxiosFetch from './useAxiosFetch'
function App(props) {
const url = 'https://www.siliconvalley-codecamp.com/rest/session/arrayonly'
const {data, loading} = useAxiosFetch(url)
// setTimeout(() => {
// window.location.href = 'https://www.google.com/';
// }, 1000)
if (loading) {
return (
<div>
Loading...
<br />
<button
onClick={() => {
props.history.Push('/home')
}}
>
switch away
</button>
</div>
)
} else {
return <div>{JSON.stringify(data)}</div>
}
}
ReactDOM.render(
<Router>
<Switch>
<Route path="/home" render={() => <div>Hello</div>} />
<Route path="/" component={App} />
</Switch>
</Router>,
document.getElementById('root'),
)
Vous pouvez check the demo
fonctionne correctement sur un réseau lent