A bug has been reported when using unstable_middleware and shouldRevalidate together in React Router v7.8.2. The issue was that the parent loader kept re-running every time I navigated to the child route, despite setting shouldRevalidate: () => false in the parent route.
Reproduction conditions were as follows.
Enable SSR mode (ssr: true)
Enable middleware (unstable_middleware: true)
Set shouldRevalidate: () => false on the parent route
Also mentioned in the issue was that this problem did not occur until v7.8.1. The changes introduced in v7.8.2 were what created this bug.
Cause analysis
I started comparing the code between v7.8.1 and v7.8.2 to analyze the differences between the versions. In particular, we focused on middleware-related code and re-verification logic.
I found the core in file packages/react-router/lib/server-runtime/single-fetch.ts
// Inside singleFetchAction function
let result = await staticHandler.query(request, {
skipRevalidation: true, // Setting the re-verification skip option
generateMiddlewareResponse: build.future.v8_middleware
? async (query) => {
try {
let innerResult = await query(handlerRequest);
// skipRevalidation not passed
return handleQueryResult(innerResult);
} catch (error) {
return handleQueryError(error);
}
}
: undefined,
});
The skipRevalidation: true option was set, but it was not being passed when calling query() inside the generateMiddlewareResponse callback.
When I traced the data flow, the flow of the problem I thought was as follows.
singleFetchAction
-> set skipRevalidation: true
-> call generateMiddlewareResponse
-> skipRevalidation missing when calling internal query()
-> shouldRevalidate is ignored and the loader is re-executed.
When I checked the type definition, there was a more fundamental problem. The skipRevalidation parameter was not defined at all in the generateMiddlewareResponse callback signature.
There was one issue while preparing for PR. According to React Router’s Contributing Guidelines,
"All new features, bug-fixes, or anything that touches react-router code should be branched off of and merged into the dev branch."
All code changes had to target the dev branch. However, the dev branch was using v8_middleware, and the main branch was using unstable_middleware.
The issue was reported for unstable_middleware, but a fix had to be made for v8_middleware to follow the guidelines. Fortunately, the two middlewares share the same code path, so modifications to v8_middleware would also apply to unstable_middleware.
Therefore, we added this to the body of the PR to convey the situation.
## Note on Middleware Selection
Per the contributing guidelines, all code changes must target the dev branch.
The dev branch uses v8_middleware (whereas main uses unstable_middleware),
so I implemented and tested the fix against v8_middleware to comply with
the guidelines. The underlying bug reproduces identically on both
unstable_middleware and v8_middleware; this PR fixes the shared path by
correctly forwarding skipRevalidation through generateMiddlewareResponse.
Feedback from maintainer
The maintainer conducted a review. He approved my approach but suggested a simpler solution,
"Thank you for the PR @joseph0926! We can fix this in a slightly simpler manner by falling back on the internal filterMatchesToLoad (which was what we missed originally in #14154)"
The maintainer solved the problem more succinctly by leveraging the internal filterMatchesToLoad. I also integrated the tests I wrote into an existing middleware test suite to make the code cleaner.
After all tests passed and the maintainer's improvements were reflected, the PR was merged. This fix is included in the v7.9.0 release.
Finish
The biggest parts of fixing the issue were “what version is normal and where does the problem start?” and “creating a test case.”
In addition to fixing the issue, I try to follow the rules below in all edits.
My modifications must never affect existing functions.
The problem should be solved.
As I followed and revised these guidelines, I think the biggest thing I felt was the importance of testing.
In fact, AI exists these days and it is possible to correct the problem if you know exactly where the problem is, but the process of figuring out what effect this fix will have is a bit laborious, and if testing is not enough, verification is not possible, so I think I have learned the importance of testing.