UNPKG

5.61 kB Markdown View Raw
1---
2title: Revalidation Optimization
3---
4
5# Revalidation Optimization
6
7[MODES: framework, data]
8
9After a mutation or some navigations, React Router re-runs loaders so
10the UI stays in sync with the server. That default is the right
11starting point. When a loader is expensive, or a mutation cannot
12affect that route's data, you can skip the reload.
13
14<docs-warning>
15Skipping revalidation can leave the UI out of sync with the server.
16Prefer targeting a specific action or navigation, and fall back to
17`defaultShouldRevalidate` instead of always returning `false`.
18</docs-warning>
19
20## Default behavior
21
22The default behavior differs between Framework and Data Modes:
23
24- **Framework Mode with SSR**
25 - Defaults to opt-out behavior - active loaders are revalidated on navigations and successful submissions ([`Link`][link], [`Form`][form], [`fetcher.submit`][use-fetcher])
26 - Failed submissions returning a 4xx/5xx status do not trigger revalidations by default
27- **Framework "SPA Mode" and Data Mode**
28 - Defaults to opt-out behavior on successful submissions - active loaders are revalidated on successful submissions ([`Form`][form], [`fetcher.submit`][use-fetcher])
29 - Failed submissions returning a 4xx/5xx status do not trigger revalidations by default
30 - Defaults to opt-in behavior for GET navigations ([`Link`][link]) - active loaders are only revalidated if their dynamic params changed, or if any search params changed
31 - A GET navigation to the _exact_ same URL is treated like a page refresh and all loaders are revalidated.
32
33Matched matched routes are handled independently - A child that skips
34revalidation does not skip any ancestor routes.
35
36[`fetcher.load`][use-fetcher] only revalidates by default after action
37submissions and explicit [`useRevalidator`][use-revalidator] calls, not
38on search-param or param-driven navigations.
39
40A plain `fetch()` to a [resource route][resource-routes] does not
41go through the router, so it does not revalidate loaders.
42
43## Skip a route with `shouldRevalidate`
44
45Export `shouldRevalidate` from the [route module][route-module]
46(Framework Mode) or set it on the [route object][data-mode]
47(Data Mode). Returning `false` skips **that route's** loader.
48
49```tsx filename=app/routes/dashboard.tsx
50// Framework Mode
51export function shouldRevalidate() {
52 return false;
53}
54```
55
56```tsx src/main.tsx
57// Data Mode
58createBrowserRouter([
59 {
60 path: "/dashboard",
61 loader: dashboardLoader,
62 shouldRevalidate: () => false,
63 Component: Dashboard,
64 },
65]);
66```
67
68Always returning `false` opts that route out of the default
69behavior completely, including cases you usually still want
70(param changes, explicit [`useRevalidator`][use-revalidator]).
71Prefer the conditional form below.
72
73## Opt out of specific requests
74
75Inspect
76[`ShouldRevalidateFunctionArgs`][should-revalidate-args]
77and return `defaultShouldRevalidate` for everything else.
78
79```tsx
80import type { ShouldRevalidateFunctionArgs } from "react-router";
81
82export function shouldRevalidate({
83 formMethod,
84 formAction,
85 defaultShouldRevalidate,
86}: ShouldRevalidateFunctionArgs) {
87 if (
88 formMethod === "POST" &&
89 formAction?.endsWith("/analytics")
90 ) {
91 return false;
92 }
93
94 return defaultShouldRevalidate;
95}
96```
97
98Other useful fields:
99
100- `formData`, `json`, `text` — the submission body
101- `actionResult`, `actionStatus` — the action's return value
102- `currentUrl`, `nextUrl`, `currentParams`, `nextParams` —
103 the navigation
104
105You can ignore search-param-only updates while still
106revalidating when the pathname changes:
107
108```tsx
109export function shouldRevalidate({
110 currentUrl,
111 nextUrl,
112 defaultShouldRevalidate,
113}: ShouldRevalidateFunctionArgs) {
114 if (currentUrl.pathname === nextUrl.pathname) {
115 return false;
116 }
117
118 return defaultShouldRevalidate;
119}
120```
121
122## Skip revalidation for one event
123
124Pass `defaultShouldRevalidate={false}` at the call site so you
125do not have to change every route file. This works on
126[`<Form>`][form], [`<Link>`][link], `<fetcher.Form>`, and as an
127option to [`useSubmit`][use-submit], `fetcher.submit`,
128[`useNavigate`][use-navigate], and
129[`useSearchParams`][use-search-params].
130
131```tsx
132import { Form, Link } from "react-router";
133
134<Link
135 to="/search?q=shoes"
136 defaultShouldRevalidate={false}
137>
138 Search Shoes
139</Link>
140
141<Form
142 method="post"
143 action="/analytics"
144 defaultShouldRevalidate={false}
145>
146 <button>Track Click</button>
147</Form>
148```
149
150```tsx
151fetcher.submit(
152 { intent: "save-progress" },
153 {
154 method: "post",
155 action: "/save-progress",
156 defaultShouldRevalidate: false,
157 },
158);
159```
160
161If a matched route does **not** export `shouldRevalidate`, this
162value is used directly for that loader. If it **does** export
163`shouldRevalidate`, the value is passed in as
164`defaultShouldRevalidate` and the route still has the final say.
165
166That is why a child `shouldRevalidate` that always returns
167`false` cannot hide a root reload after `fetcher.submit`. Either
168also opt `root` out for that case, or pass
169`defaultShouldRevalidate: false` at the call site when `root`
170has no `shouldRevalidate` of its own.
171
172[data-mode]: ../start/data/route-object#shouldrevalidate
173[form]: ../api/components/Form
174[link]: ../api/components/Link
175[resource-routes]: ./resource-routes
176[route-module]: ../start/framework/route-module#shouldrevalidate
177[should-revalidate-args]: https://api.reactrouter.com/v8/interfaces/react-router.ShouldRevalidateFunctionArgs.html
178[use-fetcher]: ../api/hooks/useFetcher
179[use-navigate]: ../api/hooks/useNavigate
180[use-revalidator]: ../api/hooks/useRevalidator
181[use-search-params]: ../api/hooks/useSearchParams
182[use-submit]: ../api/hooks/useSubmit