UNPKG

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