Cancelling XHRs with Next.js 14 and RTK 2
TL;DR
This article describes the principle of cancelling XHR requests on the example of Next.js 14 application with RTK 2. You can see the finished demo project at this link, and its source code here.
Introduction
If you’ve been involved in SPA application development you’ve probably had to deal with sending asynchronous requests. This is a fairly trivial task, send a request and get a response from the server, then render the content or handle an error if there is one. However, there are situations when in your project several pages send their requests and the user quickly switching between pages risks getting old data (e.g. if the previous lost request arrives later than the new one, it will overwrite its data). At the same time it is important that the server will suffer from an increased waste of resources to process essentially redundant operations.
With this article I am killing two birds with one stone, sharing this information with you, but also exploring the possibility for adapting features of an existing application whose tech stack is outdated to a new platform. Since the existing project is fully implemented on RTK and React, my goal was to connect Redux to Next.js 14 using a App Router which is stable nowadays. I see Next.js as an abstraction layer above React that provides the latest changes from the developers of React, transparent conventions, different rendering options and can act as a single platform for all front-end developers within an organisation.
Demo application
Step 01 — Define the requirements
Let’s build something abstract and minimalistic to demonstrate this concept at work. But first, define the requirements:
AC 1 We need at least 2 pages with separate routes.
AC 1.1 The first one will be the start page, where we will place a random illustration.
AC 1.2 The second one will be a list of elements that we get from the fake API.
AC 1.2.1 We need this list to load slowly, so that we can return to the main page and the stuck request must be cancelled.
AC 1.2.2 During loading, the indication should be rendered using the skeleton technique.
AC 1.2.3 Also, if the information has already been fetched once, we don’t want the request to be sent again and cache it.
AC 2 As a bonus, the application should be able to switch between a dark and light mode. The system mode should be applied by default.
Step 02 — Define the technical stack
- Language: TypeScript: 5.3.2
- Framework: Next.js 14.0.3
- State Management: Redux Toolkit 2.0.1
- Styling: Tailwind CSS 3.3.6
- UI Components: shadcn/ui & Radix UI
- Deployment: Vercel
Step 03 — Develop the prototype
I chose an ocean theme for the content and colour palette, so the idea is to create a list with randomly generated fake marine creatures.
Let’s perform the initial framework setup
// Create a new Next.js app
npx create-next-app@latest margine-creatures --typescript --tailwind --eslint
and UI Components core,
// Add a collection of re-usable UI components
npx shadcn-ui@latest init
after that, install through CLI the components that we are going to use in our project:
npx shadcn-ui@latest add button
npx shadcn-ui@latest add card
npx shadcn-ui@latest add hover-card
npx shadcn-ui@latest add navigation-menu
npx shadcn-ui@latest add skeleton
npx shadcn-ui@latest add toast
Eventually, implementing dark mode (AC2) using this doc.
Given that we have Next.js, it is possible to create an API endpoint as part of our project using Route Handlers and resolve AC 1.2:
As specified in AC 1.2.1, we intentionally set the delay in L9.
Regarding RTK configuration, “Redux Toolkit Setup with Next.js” documentation page helped me a lot. In the official Next.js repository you will find an example of how to use it, I leave the link below.
Also if you migrating from existing application, consider this manual.
Step 04 — Fetching and cancelling data
I recommend to use RTK Query in real projects, but for now, to keep the codebase uncomplicated let’s do a wrapper around a standard Fetch API:
As you can see, this code provides a simple utility class Http
for making HTTP GET requests with error handling, specifically redirecting to a 404 page if the response status is not OK.
In the provided code, the
signal
parameter is part of a mechanism called the AbortController. This mechanism allows the code to associate an "abort signal" with an asynchronous operation, such as an HTTP request. The purpose of this signal is to provide a way to cancel or abort the ongoing operation.Here’s why it matters:
Let’s say a user initiates an action that triggers an HTTP request, but before the request completes, the user decides to navigate away from the current page or cancel the operation. The
signal
parameter allows the code to gracefully handle this situation. If the associated signal is aborted (meaning the user has canceled the operation), the ongoing HTTP request is canceled as well. In practical terms, this helps improve the overall user experience by preventing unnecessary network requests and ensuring that resources are not wasted on operations that the user is no longer interested in.
The AbortController is generally well-supported across modern browsers.
So, let’s utilise the API endpoint and the Http
utility class in Redux Toolkit Async Thunk
and use it on the list page (AC 1.2):
As soon as the user leaves this page, unmounting will take place (L28–30) and a cancel signal will be sent to the pending request. The user will see a notification:
Step 05 — Prevent extra fetching
In terms of AC 1.2.3, we can set a validation condition and if our repository already contains data, ignore creating a new fetch request.
The user will also be informed by a notification:
Try it on you own
This application requires Node.js v18.18+
- Clone the repository:
git clone https://github.com/fomazov/marine-creatures.git
2. Install dependencies:
npm install
3. Run the development server using the command:
npm run dev
4. Open http://localhost:3000 with your browser to see the result.
That’s it. Thank you for reading and happy developing!