Skip to content

Commit 18c34ae

Browse files
committed
Once props docs
1 parent 936e9b4 commit 18c34ae

File tree

9 files changed

+447
-182
lines changed

9 files changed

+447
-182
lines changed

docs/.vitepress/config.mts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ export default defineConfig({
136136
{ text: 'Prefetching', link: '/guide/prefetching' },
137137
{ text: 'Load when visible', link: '/guide/load-when-visible' },
138138
{ text: 'Merging props', link: '/guide/merging-props' },
139+
{ text: 'Once props', link: '/guide/once-props' },
139140
{ text: 'Infinite scroll', link: '/guide/infinite-scroll' },
140141
{ text: 'Remembering state', link: '/guide/remembering-state' },
141142
],

docs/cookbook/handling-validation-error-types.md

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,10 @@ import type { InertiaFormProps as OriginalProps } from '@inertiajs/vue3'
2626
type FormDataType = Record<string, FormDataConvertible>
2727

2828
declare module '@inertiajs/vue3' {
29-
interface InertiaFormProps<TForm extends FormDataType>
30-
extends Omit<OriginalProps<TForm>, 'errors' | 'setError'> {
29+
interface InertiaFormProps<TForm extends FormDataType> extends Omit<
30+
OriginalProps<TForm>,
31+
'errors' | 'setError'
32+
> {
3133
errors: Partial<Record<FormDataKeys<TForm>, string[]>>
3234

3335
setError(field: FormDataKeys<TForm>, value: string[]): this
@@ -60,8 +62,10 @@ import type { InertiaFormProps as OriginalProps } from '@inertiajs/react'
6062
type FormDataType = Record<string, FormDataConvertible>
6163

6264
declare module '@inertiajs/react' {
63-
interface InertiaFormProps<TForm extends FormDataType>
64-
extends Omit<OriginalProps<TForm>, 'errors' | 'setError'> {
65+
interface InertiaFormProps<TForm extends FormDataType> extends Omit<
66+
OriginalProps<TForm>,
67+
'errors' | 'setError'
68+
> {
6569
errors: Partial<Record<FormDataKeys<TForm>, string[]>>
6670

6771
setError(field: FormDataKeys<TForm>, value: string[]): void
@@ -92,8 +96,10 @@ import type { Writable } from 'svelte/store'
9296
type FormDataType = Record<string, FormDataConvertible>
9397

9498
declare module '@inertiajs/svelte' {
95-
interface InertiaFormProps<TForm extends FormDataType>
96-
extends Omit<OriginalProps<TForm>, 'errors' | 'setError'> {
99+
interface InertiaFormProps<TForm extends FormDataType> extends Omit<
100+
OriginalProps<TForm>,
101+
'errors' | 'setError'
102+
> {
97103
errors: Partial<Record<FormDataKeys<TForm>, string[]>>
98104

99105
setError(field: FormDataKeys<TForm>, value: string[]): this

docs/guide/deferred-props.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,8 @@ export default () => (
121121

122122
:::
123123

124+
## Multiple Deferred Props
125+
124126
If you need to wait for multiple deferred props to become available, you can specify an array to the `data` prop.
125127

126128
:::tabs key:frameworks
@@ -189,3 +191,21 @@ export default () => (
189191
```
190192

191193
:::
194+
195+
## Combining with Once Props
196+
197+
@available_since rails=master core=2.2.20
198+
199+
You may pass the `once: true` argument to a deferred prop to ensure the data is resolved only once and remembered by the client across subsequent navigations.
200+
201+
```ruby
202+
class DashboardController < ApplicationController
203+
def index
204+
render inertia: {
205+
stats: InertiaRails.defer(once: true) { Stats.generate },
206+
}
207+
end
208+
end
209+
```
210+
211+
For more information on once props, see the [once props](/guide/once-props) documentation.

docs/guide/merging-props.md

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ You can also merge props directly on the client side without making a server req
130130

131131
## Combining with deferred props
132132

133-
You can also combine [deferred props](/guide/deferred-props) with mergeable props to defer the loading of the prop and ultimately mark it as mergeable once it's loaded.
133+
You may combine [deferred props](/guide/deferred-props) with mergeable props to defer the loading of the prop and ultimately mark it as mergeable once it's loaded.
134134

135135
```ruby
136136
class UsersController < ApplicationController
@@ -146,6 +146,24 @@ class UsersController < ApplicationController
146146
end
147147
```
148148

149+
## Combining with Once Props
150+
151+
@available_since rails=master core=2.2.20
152+
153+
You may pass the `once: true` argument to a deferred prop to ensure the data is resolved only once and remembered by the client across subsequent navigations.
154+
155+
```ruby
156+
class UsersController < ApplicationController
157+
def index
158+
render inertia: {
159+
activity: InertiaRails.merge(once: true) { @user.recent_activity },
160+
}
161+
end
162+
end
163+
```
164+
165+
For more information on once props, see the [once props](/guide/once-props) documentation.
166+
149167
## Resetting props
150168

151169
On the client side, you can indicate to the server that you would like to reset the prop. This is useful when you want to clear the prop value before merging new data, such as when the user enters a new search query on a paginated list.

docs/guide/once-props.md

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
# Once Props
2+
3+
@available_since rails=master core=2.2.20
4+
5+
Some data rarely changes, is expensive to compute, or is simply large. Rather than including this data in every response, you may use _once props_. These props are remembered by the client and reused on subsequent pages that include the same prop. This makes them ideal for [shared data](/guide/shared-data).
6+
7+
## Creating Once Props
8+
9+
To create a once prop, use the `InertiaRails.once` method when returning your response. This method receives a block that returns the prop data.
10+
11+
```ruby
12+
class BillingController < ApplicationController
13+
def index
14+
render inertia: {
15+
plans: InertiaRails.once { Plan.all },
16+
}
17+
end
18+
end
19+
```
20+
21+
After the client has received this prop, subsequent requests will skip resolving the block and exclude the prop from the response. The client only remembers once props while navigating between pages that include them.
22+
23+
Navigating to a page without the once prop will forget the remembered value, and it will be resolved again on the next page that has it. In practice, this is rarely an issue since once props are typically used as shared data or within a specific section of your application.
24+
25+
## Forcing a Refresh
26+
27+
You may force a once prop to be refreshed using the `fresh` parameter.
28+
29+
```ruby
30+
class BillingController < ApplicationController
31+
def index
32+
render inertia: {
33+
plans: InertiaRails.once(fresh: true) { Plan.all },
34+
}
35+
end
36+
end
37+
```
38+
39+
## Refreshing from the Client
40+
41+
You may refresh a once prop from the client-side using a [partial reload](/guide/partial-reloads). The server will always resolve a once prop when explicitly requested.
42+
43+
:::tabs key:frameworks
44+
== Vue
45+
46+
```js
47+
import { router } from '@inertiajs/vue3'
48+
49+
router.reload({ only: ['plans'] })
50+
```
51+
52+
== React
53+
54+
```js
55+
import { router } from '@inertiajs/react'
56+
57+
router.reload({ only: ['plans'] })
58+
```
59+
60+
== Svelte 4|Svelte 5
61+
62+
```js
63+
import { router } from '@inertiajs/svelte'
64+
65+
router.reload({ only: ['plans'] })
66+
```
67+
68+
:::
69+
70+
## Expiration
71+
72+
You may set an expiration time using the `expires_in` parameter. This parameter accepts a `Time`, `DateTime`, `ActiveSupport::Duration`, or an integer (seconds). The prop will be refreshed on a subsequent visit after the expiration time has passed.
73+
74+
```ruby
75+
class DashboardController < ApplicationController
76+
def index
77+
render inertia: {
78+
plans: InertiaRails.once(expires_in: 1.day) { Plan.all },
79+
}
80+
end
81+
end
82+
```
83+
84+
## Custom Keys
85+
86+
You may assign a custom key to the prop using the `key` parameter. This is useful when you want to share data across multiple pages while using different prop names.
87+
88+
```ruby
89+
class TeamsController < ApplicationController
90+
def index
91+
render inertia: {
92+
memberRoles: InertiaRails.once(key: 'roles') { Role.all },
93+
}
94+
end
95+
96+
def invite
97+
render inertia: {
98+
availableRoles: InertiaRails.once(key: 'roles') { Role.all },
99+
}
100+
end
101+
end
102+
```
103+
104+
Both pages share the same underlying data because they use the same custom key, so the prop is only resolved for whichever page you visit first.
105+
106+
## Sharing Once Props
107+
108+
You may share once props globally using the `inertia_share` controller method.
109+
110+
```ruby
111+
class ApplicationController < ActionController::Base
112+
inertia_share countries: InertiaRails.once { Country.all }
113+
end
114+
```
115+
116+
## Prefetching
117+
118+
Once props are compatible with [prefetching](/guide/prefetching). The client automatically includes any remembered once props in prefetched responses, so navigating to a prefetched page will already have the once props available.
119+
120+
Prefetched pages containing an expired once prop will be invalidated from the cache.
121+
122+
## Combining with Other Prop Types
123+
124+
The `once()` modifier may be chained onto [deferred](/guide/deferred-props), [merge](/guide/merging-props), and [optional](/guide/partial-reloads#lazy-data-evaluation) props.
125+
126+
```ruby
127+
class DashboardController < ApplicationController
128+
def index
129+
render inertia: {
130+
memberRoles: InertiaRails.once(key: 'roles') { Role.all },
131+
permissions: InertiaRails.defer(once: true) { Permission.all },
132+
activity: InertiaRails.merge(once: true) { @user.recent_activity },
133+
categories: InertiaRails.optional(once: true) { Category.all },
134+
}
135+
end
136+
end
137+
```

docs/guide/partial-reloads.md

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -249,29 +249,24 @@ end
249249

250250
Here's a summary of each approach:
251251

252+
| Approach | Standard Visits | Partial Reloads | Evaluated |
253+
| :---------------------------------------------------------------------------- | :-------------- | :-------------- | :--------------- |
254+
| <span style="white-space: nowrap">`User.all`</span> | Always | Optionally | Always |
255+
| <span style="white-space: nowrap">`-> { User.all }`</span> | Always | Optionally | Only when needed |
256+
| <span style="white-space: nowrap">`InertiaRails.optional { User.all }`</span> | Never | Optionally | Only when needed |
257+
| <span style="white-space: nowrap">`InertiaRails.always { User.all }`</span> | Always | Always | Always |
258+
259+
## Combining with Once Props
260+
261+
@available_since rails=master core=2.2.20
262+
263+
You may pass the `once: true` argument to a deferred prop to ensure the data is resolved only once and remembered by the client across subsequent navigations.
264+
252265
```ruby
253266
class UsersController < ApplicationController
254267
def index
255268
render inertia: {
256-
# ALWAYS included on standard visits
257-
# OPTIONALLY included on partial reloads
258-
# ALWAYS evaluated
259-
users: User.all,
260-
261-
# ALWAYS included on standard visits
262-
# OPTIONALLY included on partial reloads
263-
# ONLY evaluated when needed
264-
users: -> { User.all },
265-
266-
# NEVER included on standard visits
267-
# OPTIONALLY included on partial reloads
268-
# ONLY evaluated when needed
269-
users: InertiaRails.optional { User.all },
270-
271-
# ALWAYS included on standard visits
272-
# ALWAYS included on partial reloads
273-
# ALWAYS evaluated
274-
users: InertiaRails.always { User.all },
269+
users: InertiaRails.optional(once: true) { Users.all },
275270
}
276271
end
277272
end

docs/guide/shared-data.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,20 @@ end
8383
> [!NOTE]
8484
> Page props and shared data are merged together, so be sure to namespace your shared data appropriately to avoid collisions.
8585
86+
## Sharing Once Props
87+
88+
@available_since rails=master core=2.2.20
89+
90+
You may share data that is resolved only once and remembered by the client across subsequent navigations using [once props](/guide/once-props).
91+
92+
```ruby
93+
class ApplicationController < ActionController::Base
94+
inertia_share countries: InertiaRails.once { Country.all }
95+
end
96+
```
97+
98+
For more information on once props, see the [once props](/guide/once-props) documentation.
99+
86100
## Accessing shared data
87101

88102
Once you have shared the data server-side, you will be able to access it within any of your pages or components. Here's an example of how to access shared data in a layout component.

0 commit comments

Comments
 (0)