Reputation: 15
I am trying to cancel the request when the input in my search field is empty so the results don't show after the input is deleted.
I tried not to use cancel request and it works, the only problem is that I delete characters too fast the results don't dissapear. Declared this outside component:
const source = axios.CancelToken.source();
Also tried declaring it inside the handler same result.
This is the state :
state = {
results: null
}
And the function triggered on change
findGames = (event) => { //Cancel request when empty event.target.value missing
let searchText = event.target.value;
source.cancel('Request Canceled');
if (event.target.value !== "") {
let body = {
"search_text": `${searchText}`, "fields": ["id", "name", "release_dates"]
}
axios.post(getGameIDUrl, body, headers,{cancelToken: source.token})
.then(res => res.data.filter((result) => { return result.type === "game" }))
.then(res => this.setState({ results: res }))
.catch(err => {
if(axios.isCancel(err)) {
console.log('Request Canceled')
}
})
} else {
this.setState({results: null})
}
}
This is how I trigger the function:
<StyledInput type="text" onChange={this.findGames} />
Then I just display the results:
{this.state.results}
Upvotes: 1
Views: 5294
Reputation: 61
If your responseType is "stream" then cancelling with CancelToken or the new AbortController won't work. You have to close the stream.
With the console.log in response.data.on you can see that the log will stop, after calling response.data.destroy.
var options = {
url: url,
method: 'POST',
data: {credentials: "something"},
responseType: "stream"
}
const response = await axios(options)
setTimeout(() => {
response.data.destroy()//--> this will close the stream and the download will stop
}, 3000);
response.data.on('data', (chunk) => {
console.log("chunk length: ", chunk.length)
})
What I did is create a variable outside my request function, so that I can stop the request by changing the variable.
var cancelDownload = false
function requestFun() {
var options = {
url: url,
method: 'POST',
data: { credentials: "something" },
responseType: "stream"
}
const response = await axios(options)
response.data.on('data', (chunk) => {
console.log("chunk length: ", chunk.length)
if (cancelDownload) {
response.data.destroy()
cancelDownload = false
}
})
}
PS: Even though this post is pretty old I wanted to give my solution.
Upvotes: 2
Reputation: 23695
The reason axios.CancelToken.source();
is unique each time you press a key. So for your code: request you've sent will never be cancelled and you try to cancel request that you've not sent.
You have to preserve axios.CancelToken.source();
between calls and to me it also makes sense to cancel request unconditionally you've sent on previous keystroke.
That not only covers case with empty string but also saves us from race conditions when responses for sequential keystrokes return in wrong order.
// source is declared outside event handler to be preserved between calls
this.source.cancel('Request Canceled');
this.source = axios.CancelToken.source();
if (event.target.value) {
let body = {
"search_text": `${searchText}`,
"fields": ["id", "name", "release_dates"]
}
axios.post(getGameIDUrl, body, headers,{cancelToken: this.source.token})
.then(res => res.data.filter((result) => { return result.type === "game" }))
.then(res => this.setState({ results: res }))
.catch(err => {
if(axios.isCancel(err)) {
console.log('Request Canceled')
}
})
} else {
this.setState({ results: [] });
}
Upvotes: 2