@fringeworks/react-defer-rendering 0.8.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ja.md +313 -319
- package/README.md +313 -319
- package/_usePromiseState.cjs +13 -20
- package/_usePromiseState.d.cts +5 -2
- package/_usePromiseState.d.mts +6 -3
- package/_usePromiseState.mjs +10 -16
- package/_virtual/_rolldown/runtime.cjs +1 -2
- package/constants.cjs +0 -1
- package/constants.d.cts +3 -4
- package/constants.d.mts +3 -4
- package/index.cjs +41 -44
- package/index.d.cts +15 -4
- package/index.d.mts +15 -4
- package/index.mjs +4 -7
- package/package.json +113 -31
- package/types.cjs +1 -0
- package/types.d.cts +7 -8
- package/types.d.mts +7 -8
- package/useDeferUntilAsyncComplete/index.cjs +4 -4
- package/useDeferUntilAsyncComplete/index.d.cts +1 -1
- package/useDeferUntilAsyncComplete/index.d.mts +1 -1
- package/useDeferUntilAsyncComplete/index.mjs +1 -3
- package/useDeferUntilAsyncComplete/types.cjs +1 -0
- package/useDeferUntilAsyncComplete/types.d.cts +3 -4
- package/useDeferUntilAsyncComplete/types.d.mts +3 -4
- package/useDeferUntilAsyncComplete/useDeferUntilAsyncComplete.cjs +7 -5
- package/useDeferUntilAsyncComplete/useDeferUntilAsyncComplete.d.cts +0 -1
- package/useDeferUntilAsyncComplete/useDeferUntilAsyncComplete.d.mts +2 -4
- package/useDeferUntilAsyncComplete/useDeferUntilAsyncComplete.mjs +2 -1
- package/useDeferUntilBreakpoint/index.cjs +4 -4
- package/useDeferUntilBreakpoint/index.d.cts +1 -1
- package/useDeferUntilBreakpoint/index.d.mts +1 -1
- package/useDeferUntilBreakpoint/index.mjs +1 -3
- package/useDeferUntilBreakpoint/types.cjs +1 -0
- package/useDeferUntilBreakpoint/types.d.cts +5 -5
- package/useDeferUntilBreakpoint/types.d.mts +5 -5
- package/useDeferUntilBreakpoint/useDeferUntilBreakpoint.cjs +29 -9
- package/useDeferUntilBreakpoint/useDeferUntilBreakpoint.d.cts +0 -1
- package/useDeferUntilBreakpoint/useDeferUntilBreakpoint.d.mts +2 -4
- package/useDeferUntilBreakpoint/useDeferUntilBreakpoint.mjs +25 -6
- package/useDeferUntilCallThreshold/index.cjs +4 -4
- package/useDeferUntilCallThreshold/index.d.cts +1 -1
- package/useDeferUntilCallThreshold/index.d.mts +1 -1
- package/useDeferUntilCallThreshold/index.mjs +1 -3
- package/useDeferUntilCallThreshold/types.cjs +1 -0
- package/useDeferUntilCallThreshold/types.d.cts +3 -4
- package/useDeferUntilCallThreshold/types.d.mts +3 -4
- package/useDeferUntilCallThreshold/useDeferUntilCallThreshold.cjs +32 -19
- package/useDeferUntilCallThreshold/useDeferUntilCallThreshold.d.cts +0 -1
- package/useDeferUntilCallThreshold/useDeferUntilCallThreshold.d.mts +2 -4
- package/useDeferUntilCallThreshold/useDeferUntilCallThreshold.mjs +27 -15
- package/useDeferUntilChange/index.cjs +4 -4
- package/useDeferUntilChange/index.d.cts +1 -1
- package/useDeferUntilChange/index.d.mts +1 -1
- package/useDeferUntilChange/index.mjs +1 -3
- package/useDeferUntilChange/types.cjs +1 -0
- package/useDeferUntilChange/types.d.cts +3 -4
- package/useDeferUntilChange/types.d.mts +3 -4
- package/useDeferUntilChange/useDeferUntilChange.cjs +6 -9
- package/useDeferUntilChange/useDeferUntilChange.d.cts +0 -1
- package/useDeferUntilChange/useDeferUntilChange.d.mts +2 -4
- package/useDeferUntilChange/useDeferUntilChange.mjs +2 -4
- package/useDeferUntilDate/index.cjs +4 -4
- package/useDeferUntilDate/index.d.cts +1 -1
- package/useDeferUntilDate/index.d.mts +1 -1
- package/useDeferUntilDate/index.mjs +1 -3
- package/useDeferUntilDate/types.cjs +1 -0
- package/useDeferUntilDate/types.d.cts +3 -4
- package/useDeferUntilDate/types.d.mts +3 -4
- package/useDeferUntilDate/useDeferUntilDate.cjs +13 -4
- package/useDeferUntilDate/useDeferUntilDate.d.cts +0 -1
- package/useDeferUntilDate/useDeferUntilDate.d.mts +2 -4
- package/useDeferUntilDate/useDeferUntilDate.mjs +10 -4
- package/useDeferUntilIntersected/index.cjs +4 -4
- package/useDeferUntilIntersected/index.d.cts +1 -1
- package/useDeferUntilIntersected/index.d.mts +1 -1
- package/useDeferUntilIntersected/index.mjs +1 -3
- package/useDeferUntilIntersected/types.cjs +1 -0
- package/useDeferUntilIntersected/types.d.cts +8 -9
- package/useDeferUntilIntersected/types.d.mts +8 -9
- package/useDeferUntilIntersected/useDeferUntilIntersected.cjs +14 -13
- package/useDeferUntilIntersected/useDeferUntilIntersected.d.cts +0 -1
- package/useDeferUntilIntersected/useDeferUntilIntersected.d.mts +2 -4
- package/useDeferUntilIntersected/useDeferUntilIntersected.mjs +10 -8
- package/useDeferUntilOnReady/index.cjs +4 -4
- package/useDeferUntilOnReady/index.d.cts +1 -1
- package/useDeferUntilOnReady/index.d.mts +1 -1
- package/useDeferUntilOnReady/index.mjs +1 -3
- package/useDeferUntilOnReady/types.cjs +1 -0
- package/useDeferUntilOnReady/types.d.cts +3 -4
- package/useDeferUntilOnReady/types.d.mts +3 -4
- package/useDeferUntilOnReady/useDeferUntilOnReady.cjs +28 -21
- package/useDeferUntilOnReady/useDeferUntilOnReady.d.cts +2 -2
- package/useDeferUntilOnReady/useDeferUntilOnReady.d.mts +4 -5
- package/useDeferUntilOnReady/useDeferUntilOnReady.mjs +23 -15
- package/useDeferUntilReady/index.cjs +4 -4
- package/useDeferUntilReady/index.d.cts +1 -1
- package/useDeferUntilReady/index.d.mts +1 -1
- package/useDeferUntilReady/index.mjs +1 -3
- package/useDeferUntilReady/types.cjs +1 -0
- package/useDeferUntilReady/types.d.cts +2 -4
- package/useDeferUntilReady/types.d.mts +2 -4
- package/useDeferUntilReady/useDeferUntilReady.cjs +20 -32
- package/useDeferUntilReady/useDeferUntilReady.d.cts +0 -1
- package/useDeferUntilReady/useDeferUntilReady.d.mts +2 -4
- package/useDeferUntilReady/useDeferUntilReady.mjs +18 -29
- package/useDeferUntilRender/index.cjs +4 -4
- package/useDeferUntilRender/index.d.cts +1 -1
- package/useDeferUntilRender/index.d.mts +1 -1
- package/useDeferUntilRender/index.mjs +1 -3
- package/useDeferUntilRender/types.cjs +1 -0
- package/useDeferUntilRender/types.d.cts +5 -5
- package/useDeferUntilRender/types.d.mts +5 -5
- package/useDeferUntilRender/useDeferUntilRender.cjs +31 -12
- package/useDeferUntilRender/useDeferUntilRender.d.cts +0 -1
- package/useDeferUntilRender/useDeferUntilRender.d.mts +2 -4
- package/useDeferUntilRender/useDeferUntilRender.mjs +26 -8
- package/useDeferUntilResolved/index.cjs +4 -4
- package/useDeferUntilResolved/index.d.cts +1 -1
- package/useDeferUntilResolved/index.d.mts +1 -1
- package/useDeferUntilResolved/index.mjs +1 -3
- package/useDeferUntilResolved/types.cjs +1 -0
- package/useDeferUntilResolved/types.d.cts +3 -4
- package/useDeferUntilResolved/types.d.mts +3 -4
- package/useDeferUntilResolved/useDeferUntilResolved.cjs +8 -5
- package/useDeferUntilResolved/useDeferUntilResolved.d.cts +0 -1
- package/useDeferUntilResolved/useDeferUntilResolved.d.mts +2 -4
- package/useDeferUntilResolved/useDeferUntilResolved.mjs +3 -1
- package/useDeferUntilTimeout/index.cjs +4 -4
- package/useDeferUntilTimeout/index.d.cts +1 -1
- package/useDeferUntilTimeout/index.d.mts +1 -1
- package/useDeferUntilTimeout/index.mjs +1 -3
- package/useDeferUntilTimeout/types.cjs +1 -0
- package/useDeferUntilTimeout/types.d.cts +3 -4
- package/useDeferUntilTimeout/types.d.mts +3 -4
- package/useDeferUntilTimeout/useDeferUntilTimeout.cjs +23 -21
- package/useDeferUntilTimeout/useDeferUntilTimeout.d.cts +0 -1
- package/useDeferUntilTimeout/useDeferUntilTimeout.d.mts +2 -4
- package/useDeferUntilTimeout/useDeferUntilTimeout.mjs +16 -14
- package/useDeferUntilTrue/index.cjs +4 -4
- package/useDeferUntilTrue/index.d.cts +1 -1
- package/useDeferUntilTrue/index.d.mts +1 -1
- package/useDeferUntilTrue/index.mjs +1 -3
- package/useDeferUntilTrue/types.cjs +1 -0
- package/useDeferUntilTrue/types.d.cts +2 -4
- package/useDeferUntilTrue/types.d.mts +2 -4
- package/useDeferUntilTrue/useDeferUntilTrue.cjs +6 -5
- package/useDeferUntilTrue/useDeferUntilTrue.d.cts +0 -1
- package/useDeferUntilTrue/useDeferUntilTrue.d.mts +2 -4
- package/useDeferUntilWebFontReady/index.cjs +4 -4
- package/useDeferUntilWebFontReady/index.d.cts +1 -1
- package/useDeferUntilWebFontReady/index.d.mts +1 -1
- package/useDeferUntilWebFontReady/index.mjs +1 -3
- package/useDeferUntilWebFontReady/types.cjs +1 -0
- package/useDeferUntilWebFontReady/types.d.cts +6 -7
- package/useDeferUntilWebFontReady/types.d.mts +6 -7
- package/useDeferUntilWebFontReady/useDeferUntilWebFontReady.cjs +23 -14
- package/useDeferUntilWebFontReady/useDeferUntilWebFontReady.d.cts +0 -1
- package/useDeferUntilWebFontReady/useDeferUntilWebFontReady.d.mts +2 -4
- package/useDeferUntilWebFontReady/useDeferUntilWebFontReady.mjs +16 -9
- package/useDeferUntilScrolled/index.cjs +0 -5
- package/useDeferUntilScrolled/index.d.cts +0 -3
- package/useDeferUntilScrolled/index.d.mts +0 -3
- package/useDeferUntilScrolled/index.mjs +0 -5
- package/useDeferUntilScrolled/types.cjs +0 -0
- package/useDeferUntilScrolled/types.d.cts +0 -29
- package/useDeferUntilScrolled/types.d.mts +0 -29
- package/useDeferUntilScrolled/types.mjs +0 -1
- package/useDeferUntilScrolled/useDeferUntilScrolled.cjs +0 -94
- package/useDeferUntilScrolled/useDeferUntilScrolled.d.cts +0 -14
- package/useDeferUntilScrolled/useDeferUntilScrolled.d.mts +0 -15
- package/useDeferUntilScrolled/useDeferUntilScrolled.mjs +0 -91
package/README.md
CHANGED
|
@@ -1,319 +1,313 @@
|
|
|
1
|
-
# @fringeworks/react-defer-rendering
|
|
2
|
-
|
|
3
|
-
`@fringeworks/react-defer-rendering` is a
|
|
4
|
-
It provides a set of hooks covering a wide range of conditions, including timers, Promises, and browser APIs (`matchMedia`, `IntersectionObserver`, etc.).
|
|
5
|
-
|
|
6
|
-
**[日本語のREADMEはこちら](./README.ja.md)**
|
|
7
|
-
|
|
8
|
-
## Installation
|
|
9
|
-
|
|
10
|
-
```bash
|
|
11
|
-
npm install @fringeworks/react-defer-rendering
|
|
12
|
-
# or
|
|
13
|
-
pnpm add @fringeworks/react-defer-rendering
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
## Usage
|
|
17
|
-
|
|
18
|
-
Each hook returns a `state` (`'pending' | 'fallback' | 'ready'`) representing the rendering status of the target node, along with the `node` that should be rendered for that state.
|
|
19
|
-
|
|
20
|
-
```tsx
|
|
21
|
-
import { useDeferUntilTrue } from '@fringeworks/react-defer-rendering';
|
|
22
|
-
|
|
23
|
-
const { node } = useDeferUntilTrue(<MyComponent />, isReady, {
|
|
24
|
-
pending: <Spinner />,
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
return node;
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
- While the condition is not met (`pending`), the node specified in the `pending` option is rendered
|
|
31
|
-
- Once the condition is met (`ready`), rendering switches to the target node passed as the first argument
|
|
32
|
-
- Some hooks also support a failure state (`fallback`)
|
|
33
|
-
|
|
34
|
-
### When the target to wait for is not specified
|
|
35
|
-
|
|
36
|
-
Hooks that take something to wait for become `ready` immediately when the second argument is `null` / `undefined`, since there is nothing to wait for. If it later becomes unspecified, the state returns to `ready`; if it changes from unspecified to a value, the hook starts waiting again from `pending`.\
|
|
37
|
-
Because hooks cannot be called conditionally, you can make deferred rendering an optional feature of your component by passing `undefined` as the second argument.
|
|
38
|
-
|
|
39
|
-
| Hook | Second argument treated as unspecified |
|
|
40
|
-
| -------------------------------------------------------------------------------------------------- | ------------------------------------------------- |
|
|
41
|
-
| `useDeferUntilTimeout`, `useDeferUntilDate`, `useDeferUntilResolved`, `useDeferUntilAsyncComplete` | `null` / `undefined` |
|
|
42
|
-
| `useDeferUntilBreakpoint`, `useDeferUntilRender` | `null` / `undefined` / empty string |
|
|
43
|
-
| `useDeferUntilWebFontReady` | `null` / `undefined` / empty string / empty array |
|
|
44
|
-
| `useDeferUntilIntersected
|
|
45
|
-
|
|
46
|
-
For `useDeferUntilIntersected
|
|
47
|
-
|
|
48
|
-
```tsx
|
|
49
|
-
function Heading({ fontFamily, children }: Props) {
|
|
50
|
-
// Render immediately when fontFamily is not specified
|
|
51
|
-
const { node } = useDeferUntilWebFontReady(<h1>{children}</h1>, fontFamily, {
|
|
52
|
-
pending: null,
|
|
53
|
-
});
|
|
54
|
-
return node;
|
|
55
|
-
}
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
Note that the second argument of `useDeferUntilTrue` is the condition itself, so `null` / `undefined` is treated as unmet (`pending`).
|
|
59
|
-
|
|
60
|
-
## Basic Hooks
|
|
61
|
-
|
|
62
|
-
### `useDeferUntilReady`
|
|
63
|
-
|
|
64
|
-
The most fundamental hook, controlling rendering by directly specifying a `state` (`'pending' | 'fallback' | 'ready'`). All other hooks are built on top of this one.
|
|
65
|
-
|
|
66
|
-
```tsx
|
|
67
|
-
import { useDeferUntilReady } from '@fringeworks/react-defer-rendering';
|
|
68
|
-
|
|
69
|
-
const { node } = useDeferUntilReady(<MyComponent />, state, {
|
|
70
|
-
pending: <Spinner />,
|
|
71
|
-
fallback: <ErrorMessage />,
|
|
72
|
-
});
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
### `useDeferUntilTrue`
|
|
76
|
-
|
|
77
|
-
Defers rendering until a boolean condition becomes `true`.
|
|
78
|
-
|
|
79
|
-
```tsx
|
|
80
|
-
import { useDeferUntilTrue } from '@fringeworks/react-defer-rendering';
|
|
81
|
-
|
|
82
|
-
const { node } = useDeferUntilTrue(<MyComponent />, isReady, {
|
|
83
|
-
pending: <Spinner />,
|
|
84
|
-
});
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
## Waiting for Elapsed Time
|
|
88
|
-
|
|
89
|
-
### `useDeferUntilTimeout`
|
|
90
|
-
|
|
91
|
-
Defers rendering until the specified duration (in milliseconds) has elapsed.
|
|
92
|
-
|
|
93
|
-
```tsx
|
|
94
|
-
import { useDeferUntilTimeout } from '@fringeworks/react-defer-rendering';
|
|
95
|
-
|
|
96
|
-
const { node } = useDeferUntilTimeout(<MyComponent />, 3000, {
|
|
97
|
-
pending: <Spinner />,
|
|
98
|
-
});
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
### `useDeferUntilDate`
|
|
102
|
-
|
|
103
|
-
Defers rendering until the specified date and time.
|
|
104
|
-
|
|
105
|
-
```tsx
|
|
106
|
-
import { useDeferUntilDate } from '@fringeworks/react-defer-rendering';
|
|
107
|
-
|
|
108
|
-
const { node } = useDeferUntilDate(
|
|
109
|
-
<Campaign />,
|
|
110
|
-
new Date('2026-01-01T00:00:00'),
|
|
111
|
-
{ pending: <ComingSoon /> },
|
|
112
|
-
);
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
## Waiting for Async Completion
|
|
116
|
-
|
|
117
|
-
### `useDeferUntilResolved`
|
|
118
|
-
|
|
119
|
-
Defers rendering until a Promise settles (resolves or rejects)
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
)
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
<
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
<
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
```
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
```
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
##
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
`useDeferUntilBreakpoint`, `useDeferUntilIntersected`, `useDeferUntilScrolled`, and `useDeferUntilRender` depend on browser APIs (`matchMedia`, `IntersectionObserver`, `MutationObserver`, etc.), so their actual state cannot be determined on the server. These hooks accept an `initialCondition` option to specify the value used during SSR (it is treated as `false` if not specified). Similarly, `useDeferUntilWebFontReady` accepts an `initialState` option to specify its initial state during SSR.
|
|
316
|
-
|
|
317
|
-
## License
|
|
318
|
-
|
|
319
|
-
MIT
|
|
1
|
+
# @fringeworks/react-defer-rendering
|
|
2
|
+
|
|
3
|
+
`@fringeworks/react-defer-rendering` is a library some will find handy, specialized in deferring the rendering of components until a specified condition is met.\
|
|
4
|
+
It provides a set of hooks covering a wide range of conditions, including timers, Promises, and browser APIs (`matchMedia`, `IntersectionObserver`, etc.).
|
|
5
|
+
|
|
6
|
+
**[日本語のREADMEはこちら](./README.ja.md)**
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install @fringeworks/react-defer-rendering
|
|
12
|
+
# or
|
|
13
|
+
pnpm add @fringeworks/react-defer-rendering
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Usage
|
|
17
|
+
|
|
18
|
+
Each hook returns a `state` (`'pending' | 'fallback' | 'ready'`) representing the rendering status of the target node, along with the `node` that should be rendered for that state.
|
|
19
|
+
|
|
20
|
+
```tsx
|
|
21
|
+
import { useDeferUntilTrue } from '@fringeworks/react-defer-rendering';
|
|
22
|
+
|
|
23
|
+
const { node } = useDeferUntilTrue(<MyComponent />, isReady, {
|
|
24
|
+
pending: <Spinner />,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
return node;
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
- While the condition is not met (`pending`), the node specified in the `pending` option is rendered
|
|
31
|
+
- Once the condition is met (`ready`), rendering switches to the target node passed as the first argument
|
|
32
|
+
- Some hooks also support a failure state (`fallback`)
|
|
33
|
+
|
|
34
|
+
### When the target to wait for is not specified
|
|
35
|
+
|
|
36
|
+
Hooks that take something to wait for become `ready` immediately when the second argument is `null` / `undefined`, since there is nothing to wait for. If it later becomes unspecified, the state returns to `ready`; if it changes from unspecified to a value, the hook starts waiting again from `pending`.\
|
|
37
|
+
Because hooks cannot be called conditionally, you can make deferred rendering an optional feature of your component by passing `undefined` as the second argument.
|
|
38
|
+
|
|
39
|
+
| Hook | Second argument treated as unspecified |
|
|
40
|
+
| -------------------------------------------------------------------------------------------------- | ------------------------------------------------- |
|
|
41
|
+
| `useDeferUntilTimeout`, `useDeferUntilDate`, `useDeferUntilResolved`, `useDeferUntilAsyncComplete` | `null` / `undefined` |
|
|
42
|
+
| `useDeferUntilBreakpoint`, `useDeferUntilRender` | `null` / `undefined` / empty string |
|
|
43
|
+
| `useDeferUntilWebFontReady` | `null` / `undefined` / empty string / empty array |
|
|
44
|
+
| `useDeferUntilIntersected` | The ref object itself is `null` / `undefined` |
|
|
45
|
+
|
|
46
|
+
For `useDeferUntilIntersected`, a ref whose `current` is `null` means the element has not been mounted yet, so the state stays `pending`.
|
|
47
|
+
|
|
48
|
+
```tsx
|
|
49
|
+
function Heading({ fontFamily, children }: Props) {
|
|
50
|
+
// Render immediately when fontFamily is not specified
|
|
51
|
+
const { node } = useDeferUntilWebFontReady(<h1>{children}</h1>, fontFamily, {
|
|
52
|
+
pending: null,
|
|
53
|
+
});
|
|
54
|
+
return node;
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Note that the second argument of `useDeferUntilTrue` is the condition itself, so `null` / `undefined` is treated as unmet (`pending`).
|
|
59
|
+
|
|
60
|
+
## Basic Hooks
|
|
61
|
+
|
|
62
|
+
### `useDeferUntilReady`
|
|
63
|
+
|
|
64
|
+
The most fundamental hook, controlling rendering by directly specifying a `state` (`'pending' | 'fallback' | 'ready'`). All other hooks are built on top of this one.
|
|
65
|
+
|
|
66
|
+
```tsx
|
|
67
|
+
import { useDeferUntilReady } from '@fringeworks/react-defer-rendering';
|
|
68
|
+
|
|
69
|
+
const { node } = useDeferUntilReady(<MyComponent />, state, {
|
|
70
|
+
pending: <Spinner />,
|
|
71
|
+
fallback: <ErrorMessage />,
|
|
72
|
+
});
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### `useDeferUntilTrue`
|
|
76
|
+
|
|
77
|
+
Defers rendering until a boolean condition becomes `true`.
|
|
78
|
+
|
|
79
|
+
```tsx
|
|
80
|
+
import { useDeferUntilTrue } from '@fringeworks/react-defer-rendering';
|
|
81
|
+
|
|
82
|
+
const { node } = useDeferUntilTrue(<MyComponent />, isReady, {
|
|
83
|
+
pending: <Spinner />,
|
|
84
|
+
});
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Waiting for Elapsed Time
|
|
88
|
+
|
|
89
|
+
### `useDeferUntilTimeout`
|
|
90
|
+
|
|
91
|
+
Defers rendering until the specified duration (in milliseconds) has elapsed.
|
|
92
|
+
|
|
93
|
+
```tsx
|
|
94
|
+
import { useDeferUntilTimeout } from '@fringeworks/react-defer-rendering';
|
|
95
|
+
|
|
96
|
+
const { node } = useDeferUntilTimeout(<MyComponent />, 3000, {
|
|
97
|
+
pending: <Spinner />,
|
|
98
|
+
});
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### `useDeferUntilDate`
|
|
102
|
+
|
|
103
|
+
Defers rendering until the specified date and time.
|
|
104
|
+
|
|
105
|
+
```tsx
|
|
106
|
+
import { useDeferUntilDate } from '@fringeworks/react-defer-rendering';
|
|
107
|
+
|
|
108
|
+
const { node } = useDeferUntilDate(
|
|
109
|
+
<Campaign />,
|
|
110
|
+
new Date('2026-01-01T00:00:00'),
|
|
111
|
+
{ pending: <ComingSoon /> },
|
|
112
|
+
);
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Waiting for Async Completion
|
|
116
|
+
|
|
117
|
+
### `useDeferUntilResolved`
|
|
118
|
+
|
|
119
|
+
Defers rendering until a Promise settles (resolves or rejects).\
|
|
120
|
+
Since waiting restarts whenever the Promise reference changes, do not create a new Promise on every render (create it outside the component, or memoize it with `useMemo` or similar).
|
|
121
|
+
|
|
122
|
+
```tsx
|
|
123
|
+
import { useDeferUntilResolved } from '@fringeworks/react-defer-rendering';
|
|
124
|
+
|
|
125
|
+
const { node } = useDeferUntilResolved(<MyComponent />, fetchPromise, {
|
|
126
|
+
pending: <Spinner />,
|
|
127
|
+
fallback: <ErrorMessage />,
|
|
128
|
+
});
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### `useDeferUntilAsyncComplete`
|
|
132
|
+
|
|
133
|
+
Defers rendering until an async function finishes executing. `asyncFn` is invoked after mount (inside an effect), which also manages the creation of the Promise itself. It is not invoked during SSR.\
|
|
134
|
+
Since `asyncFn` is re-invoked whenever its reference changes, memoize it with `useCallback` or similar.
|
|
135
|
+
|
|
136
|
+
```tsx
|
|
137
|
+
import { useDeferUntilAsyncComplete } from '@fringeworks/react-defer-rendering';
|
|
138
|
+
import { useCallback } from 'react';
|
|
139
|
+
|
|
140
|
+
const loadData = useCallback(
|
|
141
|
+
() => fetch('/api/data').then((res) => res.json()),
|
|
142
|
+
[],
|
|
143
|
+
);
|
|
144
|
+
const { node } = useDeferUntilAsyncComplete(<MyComponent />, loadData, {
|
|
145
|
+
pending: <Spinner />,
|
|
146
|
+
});
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Waiting for a Value to Change
|
|
150
|
+
|
|
151
|
+
### `useDeferUntilChange`
|
|
152
|
+
|
|
153
|
+
Defers rendering until the specified value changes. Each time the value changes, it briefly passes through the `pending` state.
|
|
154
|
+
|
|
155
|
+
```tsx
|
|
156
|
+
import { useDeferUntilChange } from '@fringeworks/react-defer-rendering';
|
|
157
|
+
|
|
158
|
+
const { node } = useDeferUntilChange(<Toast>{message}</Toast>, message, {
|
|
159
|
+
pending: null,
|
|
160
|
+
});
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Manual Control at Any Timing
|
|
164
|
+
|
|
165
|
+
### `useDeferUntilOnReady`
|
|
166
|
+
|
|
167
|
+
Lets you control the state at any timing by calling the returned `onReady` / `onFallback` / `onPending` handlers. Useful for conditions the hook itself cannot detect, such as event handlers.\
|
|
168
|
+
Even when state changes are delayed with `onReadyDelay` / `onFallbackDelay` / `onPendingDelay`, calling any handler cancels the pending change, so the state ends up as the one of the last handler called.
|
|
169
|
+
|
|
170
|
+
```tsx
|
|
171
|
+
import { useDeferUntilOnReady } from '@fringeworks/react-defer-rendering';
|
|
172
|
+
|
|
173
|
+
const { node, onReady } = useDeferUntilOnReady(<Video />, {
|
|
174
|
+
pending: <Spinner />,
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
<video onCanPlay={onReady}>{node}</video>;
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### `useDeferUntilCallThreshold`
|
|
181
|
+
|
|
182
|
+
Similar to `useDeferUntilOnReady`, but the state only switches once each handler has been called the specified number of times.
|
|
183
|
+
|
|
184
|
+
```tsx
|
|
185
|
+
import { useDeferUntilCallThreshold } from '@fringeworks/react-defer-rendering';
|
|
186
|
+
|
|
187
|
+
const { node, onReady } = useDeferUntilCallThreshold(<Gallery />, {
|
|
188
|
+
pending: <Spinner />,
|
|
189
|
+
onReadyCount: 3, // becomes ready once onReady has been called 3 times
|
|
190
|
+
});
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
## Waiting for Browser State
|
|
194
|
+
|
|
195
|
+
> **Note (SSR / RSC):** The hooks listed here depend on browser APIs, so their actual state cannot be determined on the server. See [Using with SSR / RSC](#using-with-ssr--rsc) for details.
|
|
196
|
+
|
|
197
|
+
### `useDeferUntilBreakpoint`
|
|
198
|
+
|
|
199
|
+
Defers rendering until a media query matches.
|
|
200
|
+
|
|
201
|
+
```tsx
|
|
202
|
+
import { useDeferUntilBreakpoint } from '@fringeworks/react-defer-rendering';
|
|
203
|
+
|
|
204
|
+
const { node } = useDeferUntilBreakpoint(
|
|
205
|
+
<DesktopNav />,
|
|
206
|
+
'(min-width: 1024px)',
|
|
207
|
+
{ pending: <MobileNav /> },
|
|
208
|
+
);
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### `useDeferUntilIntersected`
|
|
212
|
+
|
|
213
|
+
Defers rendering until a reference element enters the viewport.
|
|
214
|
+
|
|
215
|
+
```tsx
|
|
216
|
+
import { useRef } from 'react';
|
|
217
|
+
import { useDeferUntilIntersected } from '@fringeworks/react-defer-rendering';
|
|
218
|
+
|
|
219
|
+
const elementRef = useRef<HTMLDivElement>(null);
|
|
220
|
+
const { node } = useDeferUntilIntersected(<HeavyChart />, elementRef, {
|
|
221
|
+
pending: <Placeholder />,
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
<div ref={elementRef}>{node}</div>;
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
Use `rootMargin` to expand the area to check. A number is in pixels (the same value on all sides), and a string uses the `rootMargin` syntax of `IntersectionObserver` (such as `'100px 0px'` or `'10%'`; only `px` and `%` are allowed). Use `threshold` (0 to 1, defaults to `0.1`) to specify how much of the element must be visible.
|
|
228
|
+
|
|
229
|
+
### `useDeferUntilRender`
|
|
230
|
+
|
|
231
|
+
Defers rendering until an element matching the selector is rendered into the DOM. Useful for waiting on elements outside of your own control, such as ones inserted by third-party scripts.
|
|
232
|
+
|
|
233
|
+
```tsx
|
|
234
|
+
import { useDeferUntilRender } from '@fringeworks/react-defer-rendering';
|
|
235
|
+
|
|
236
|
+
const { node } = useDeferUntilRender(<Overlay />, '#third-party-widget', {
|
|
237
|
+
pending: null,
|
|
238
|
+
});
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### `useDeferUntilWebFontReady`
|
|
242
|
+
|
|
243
|
+
Defers rendering until all specified web fonts become available. Web fonts are checked with [`@fringeworks/web-font-observer`](https://www.npmjs.com/package/@fringeworks/web-font-observer).\
|
|
244
|
+
It targets web fonts defined with `@font-face`. System fonts cannot be detected as loaded, so specifying one results in `fallback` after the timeout.
|
|
245
|
+
|
|
246
|
+
```tsx
|
|
247
|
+
import { useDeferUntilWebFontReady } from '@fringeworks/react-defer-rendering';
|
|
248
|
+
|
|
249
|
+
const { node } = useDeferUntilWebFontReady(
|
|
250
|
+
<Heading>Title</Heading>,
|
|
251
|
+
'Noto Sans JP',
|
|
252
|
+
{
|
|
253
|
+
pending: <Heading style={{ visibility: 'hidden' }}>Title</Heading>,
|
|
254
|
+
},
|
|
255
|
+
);
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
To wait for multiple web fonts, pass an array. To specify the weight and so on, pass an object. `null` / `undefined` in the array are ignored, so you can add web fonts conditionally.\
|
|
259
|
+
Arrays and objects can be written inline (waiting does not restart unless their contents change).
|
|
260
|
+
|
|
261
|
+
```tsx
|
|
262
|
+
const { node } = useDeferUntilWebFontReady(<Article />, [
|
|
263
|
+
{ family: 'Noto Sans JP', text: 'あ' },
|
|
264
|
+
{ family: 'Roboto', weight: 400 },
|
|
265
|
+
isBold ? { family: 'Roboto', weight: 700 } : null,
|
|
266
|
+
]);
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
| Option | Type | Default | Description |
|
|
270
|
+
| --------------- | ---------------------------------------- | ----------- | -------------------------------------------------------------------------------------------------------- |
|
|
271
|
+
| `timeout?` | `number` | `3000` | Time in milliseconds before the state becomes `fallback` when a web font is not available |
|
|
272
|
+
| `loader?` | `(signal: AbortSignal) => Promise<void>` | - | Function that loads the web fonts. Checking starts after it completes. Called only when the fonts change |
|
|
273
|
+
| `initialState?` | `'pending' \| 'ready' \| 'fallback'` | `'pending'` | Initial state in environments where the loading state cannot be determined, such as SSR |
|
|
274
|
+
|
|
275
|
+
`weight` / `style` / `width` / `text` in the object form are the same as in `@fringeworks/web-font-observer`.
|
|
276
|
+
|
|
277
|
+
- If any web font does not become available, the state becomes `fallback` and the remaining waits are aborted.
|
|
278
|
+
- Web fonts that are already loaded become `ready` without waiting (no flicker for cached fonts).
|
|
279
|
+
- If a web font finishes loading after the state became `fallback`, the state becomes `ready`. Use `preserveOnceFallback` to keep `fallback`.
|
|
280
|
+
- Place the `@font-face` definitions outside the deferred node (e.g. global CSS or a layout). If they are inside the deferred node, they are never registered and the hook waits until the timeout.
|
|
281
|
+
|
|
282
|
+
## Common Options
|
|
283
|
+
|
|
284
|
+
### Options for `pending` / `ready`
|
|
285
|
+
|
|
286
|
+
These options are shared across all hooks.
|
|
287
|
+
|
|
288
|
+
| Option | Type | Description |
|
|
289
|
+
| -------------------- | ----------- | ---------------------------------------------- |
|
|
290
|
+
| `pending?` | `ReactNode` | Node rendered while waiting for the condition |
|
|
291
|
+
| `pendingDefer?` | `number` | Delay (in ms) before showing `pending` |
|
|
292
|
+
| `readyDefer?` | `number` | Delay (in ms) before showing the target node |
|
|
293
|
+
| `preserveOnceReady?` | `boolean` | Whether to keep the `ready` state once reached |
|
|
294
|
+
|
|
295
|
+
### Options for `fallback`
|
|
296
|
+
|
|
297
|
+
`useDeferUntilReady`, `useDeferUntilResolved`, `useDeferUntilAsyncComplete`, `useDeferUntilOnReady`, `useDeferUntilCallThreshold`, and `useDeferUntilWebFontReady` also support a `fallback` state for failures.
|
|
298
|
+
|
|
299
|
+
| Option | Type | Description |
|
|
300
|
+
| ----------------------- | ----------- | ------------------------------------------------- |
|
|
301
|
+
| `fallback?` | `ReactNode` | Node rendered on failure |
|
|
302
|
+
| `fallbackDefer?` | `number` | Delay (in ms) before showing `fallback` |
|
|
303
|
+
| `preserveOnceFallback?` | `boolean` | Whether to keep the `fallback` state once reached |
|
|
304
|
+
|
|
305
|
+
## Using with SSR / RSC
|
|
306
|
+
|
|
307
|
+
Every hook in this library ships with a `'use client'` directive attached at build time. In React Server Components environments such as Next.js App Router, they are treated as client component boundaries without any additional configuration.
|
|
308
|
+
|
|
309
|
+
`useDeferUntilBreakpoint`, `useDeferUntilIntersected`, and `useDeferUntilRender` depend on browser APIs (`matchMedia`, `IntersectionObserver`, `MutationObserver`, etc.), so their actual state cannot be determined on the server. These hooks accept an `initialCondition` option to specify the value used during SSR (it is treated as `false` if not specified). Similarly, `useDeferUntilWebFontReady` accepts an `initialState` option to specify its initial state during SSR.
|
|
310
|
+
|
|
311
|
+
## License
|
|
312
|
+
|
|
313
|
+
MIT
|