I am using Next.js v12 and Axios to fetch data in my application.
When the navigation menu is used, a request is sent using Axios each time router.query.pid changes. However, if the request for test 5
takes 5 seconds to get a response and I quickly switch to test 1
, both requests (test 1 and test 5) are logged in the console.
I want to cancel the previous request (test 5) when router.query.pid changes. The reason why I want to cancel the previous request is that in my actual project, I am saving the data using useState
, and it renders test 5
data on the test 1
page.
Here is a link to the sandbox: https://codesandbox.io/p/devbox/holy-lake-kz5xyl?workspaceId=3d3e0e81-4316-413b-a7bb-bb3ed2f5ed39
Here is my current code:
(front):
import React, { useEffect } from "react";
import Link from "next/link";
import { useRouter } from "next/router";
import axios from "axios";
const Test = () => {
const router = useRouter();
const fectchData = async (sec) => {
try {
const abortController = new AbortController();
const signal = abortController.signal;
const res = await axios.get(
`/api/test?pid=${router.query.pid}&sec=${sec}`,
signal,
);
console.log(res.data);
} catch (error) {
console.log(error);
}
return () => {
abortController.abort();
};
};
useEffect(() => {
if (router.query.pid === "1") {
fectchData(1000);
}
if (router.query.pid === "2") {
fectchData(2000);
}
if (router.query.pid === "5") {
fectchData(5000);
}
}, [router.query.pid]);
return (
<>
<ul>
<li>
<Link href="/test?pid=1">test1</Link>
</li>
<li>
<Link href="/test?pid=2">test2</Link>
</li>
<li>
<Link href="/test?pid=5">test5</Link>
</li>
</ul>
</>
);
};
export default Test;
(api):
const handler = async (req, res) => {
const { method } = req;
switch (method) {
case "GET":
return getTest(req, res);
default:
return res.status(405).json("Method not allowed");
}
};
const getTest = async (req, res) => {
const { sec } = req.query;
return setTimeout(() => {
res.status(200).send({ pid: req.query.pid, sec });
}, sec);
};
export default handler;
Problem:
When switching between test 5
and test 1
quickly, both requests are logged. I want to ensure the previous request is canceled when router.query.pid
changes.
Question:
How can I modify my code to cancel the previous Axios request when router.query.pid
changes?
move fetch function into useEffect, define controller top of fetch function and abort request when the id changed.
the mistake you made is you are returning cancel from your fetch function and also waiting for response from your fetch, so you can not access to it until your request take in place.
import React, { useEffect } from "react";
import Link from "next/link";
import { useRouter } from "next/router";
import axios from "axios";
const Test = () => {
const router = useRouter();
useEffect(() => {
const abortController = new AbortController();
const fectchData = async (sec) => {
try {
const res = await axios.get(`/api/test`, {
signal: abortController.signal,
params: { pid: router.query.pid, sec },
});
console.log(res.data);
} catch (error) {
console.log(error);
}
};
fectchData(+router.query.pid * 1000);
return () => {
console.log("Cancel", router.query.pid);
abortController.abort();
};
}, [router.query.pid]);
return (
<>
<ul>
<li>
<Link href="/test?pid=1">test1</Link>
</li>
<li>
<Link href="/test?pid=2">test2</Link>
</li>
<li>
<Link href="/test?pid=5">test5</Link>
</li>
</ul>
</>
);
};
export default Test;