@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.
Files changed (172) hide show
  1. package/README.ja.md +313 -319
  2. package/README.md +313 -319
  3. package/_usePromiseState.cjs +13 -20
  4. package/_usePromiseState.d.cts +5 -2
  5. package/_usePromiseState.d.mts +6 -3
  6. package/_usePromiseState.mjs +10 -16
  7. package/_virtual/_rolldown/runtime.cjs +1 -2
  8. package/constants.cjs +0 -1
  9. package/constants.d.cts +3 -4
  10. package/constants.d.mts +3 -4
  11. package/index.cjs +41 -44
  12. package/index.d.cts +15 -4
  13. package/index.d.mts +15 -4
  14. package/index.mjs +4 -7
  15. package/package.json +113 -31
  16. package/types.cjs +1 -0
  17. package/types.d.cts +7 -8
  18. package/types.d.mts +7 -8
  19. package/useDeferUntilAsyncComplete/index.cjs +4 -4
  20. package/useDeferUntilAsyncComplete/index.d.cts +1 -1
  21. package/useDeferUntilAsyncComplete/index.d.mts +1 -1
  22. package/useDeferUntilAsyncComplete/index.mjs +1 -3
  23. package/useDeferUntilAsyncComplete/types.cjs +1 -0
  24. package/useDeferUntilAsyncComplete/types.d.cts +3 -4
  25. package/useDeferUntilAsyncComplete/types.d.mts +3 -4
  26. package/useDeferUntilAsyncComplete/useDeferUntilAsyncComplete.cjs +7 -5
  27. package/useDeferUntilAsyncComplete/useDeferUntilAsyncComplete.d.cts +0 -1
  28. package/useDeferUntilAsyncComplete/useDeferUntilAsyncComplete.d.mts +2 -4
  29. package/useDeferUntilAsyncComplete/useDeferUntilAsyncComplete.mjs +2 -1
  30. package/useDeferUntilBreakpoint/index.cjs +4 -4
  31. package/useDeferUntilBreakpoint/index.d.cts +1 -1
  32. package/useDeferUntilBreakpoint/index.d.mts +1 -1
  33. package/useDeferUntilBreakpoint/index.mjs +1 -3
  34. package/useDeferUntilBreakpoint/types.cjs +1 -0
  35. package/useDeferUntilBreakpoint/types.d.cts +5 -5
  36. package/useDeferUntilBreakpoint/types.d.mts +5 -5
  37. package/useDeferUntilBreakpoint/useDeferUntilBreakpoint.cjs +29 -9
  38. package/useDeferUntilBreakpoint/useDeferUntilBreakpoint.d.cts +0 -1
  39. package/useDeferUntilBreakpoint/useDeferUntilBreakpoint.d.mts +2 -4
  40. package/useDeferUntilBreakpoint/useDeferUntilBreakpoint.mjs +25 -6
  41. package/useDeferUntilCallThreshold/index.cjs +4 -4
  42. package/useDeferUntilCallThreshold/index.d.cts +1 -1
  43. package/useDeferUntilCallThreshold/index.d.mts +1 -1
  44. package/useDeferUntilCallThreshold/index.mjs +1 -3
  45. package/useDeferUntilCallThreshold/types.cjs +1 -0
  46. package/useDeferUntilCallThreshold/types.d.cts +3 -4
  47. package/useDeferUntilCallThreshold/types.d.mts +3 -4
  48. package/useDeferUntilCallThreshold/useDeferUntilCallThreshold.cjs +32 -19
  49. package/useDeferUntilCallThreshold/useDeferUntilCallThreshold.d.cts +0 -1
  50. package/useDeferUntilCallThreshold/useDeferUntilCallThreshold.d.mts +2 -4
  51. package/useDeferUntilCallThreshold/useDeferUntilCallThreshold.mjs +27 -15
  52. package/useDeferUntilChange/index.cjs +4 -4
  53. package/useDeferUntilChange/index.d.cts +1 -1
  54. package/useDeferUntilChange/index.d.mts +1 -1
  55. package/useDeferUntilChange/index.mjs +1 -3
  56. package/useDeferUntilChange/types.cjs +1 -0
  57. package/useDeferUntilChange/types.d.cts +3 -4
  58. package/useDeferUntilChange/types.d.mts +3 -4
  59. package/useDeferUntilChange/useDeferUntilChange.cjs +6 -9
  60. package/useDeferUntilChange/useDeferUntilChange.d.cts +0 -1
  61. package/useDeferUntilChange/useDeferUntilChange.d.mts +2 -4
  62. package/useDeferUntilChange/useDeferUntilChange.mjs +2 -4
  63. package/useDeferUntilDate/index.cjs +4 -4
  64. package/useDeferUntilDate/index.d.cts +1 -1
  65. package/useDeferUntilDate/index.d.mts +1 -1
  66. package/useDeferUntilDate/index.mjs +1 -3
  67. package/useDeferUntilDate/types.cjs +1 -0
  68. package/useDeferUntilDate/types.d.cts +3 -4
  69. package/useDeferUntilDate/types.d.mts +3 -4
  70. package/useDeferUntilDate/useDeferUntilDate.cjs +13 -4
  71. package/useDeferUntilDate/useDeferUntilDate.d.cts +0 -1
  72. package/useDeferUntilDate/useDeferUntilDate.d.mts +2 -4
  73. package/useDeferUntilDate/useDeferUntilDate.mjs +10 -4
  74. package/useDeferUntilIntersected/index.cjs +4 -4
  75. package/useDeferUntilIntersected/index.d.cts +1 -1
  76. package/useDeferUntilIntersected/index.d.mts +1 -1
  77. package/useDeferUntilIntersected/index.mjs +1 -3
  78. package/useDeferUntilIntersected/types.cjs +1 -0
  79. package/useDeferUntilIntersected/types.d.cts +8 -9
  80. package/useDeferUntilIntersected/types.d.mts +8 -9
  81. package/useDeferUntilIntersected/useDeferUntilIntersected.cjs +14 -13
  82. package/useDeferUntilIntersected/useDeferUntilIntersected.d.cts +0 -1
  83. package/useDeferUntilIntersected/useDeferUntilIntersected.d.mts +2 -4
  84. package/useDeferUntilIntersected/useDeferUntilIntersected.mjs +10 -8
  85. package/useDeferUntilOnReady/index.cjs +4 -4
  86. package/useDeferUntilOnReady/index.d.cts +1 -1
  87. package/useDeferUntilOnReady/index.d.mts +1 -1
  88. package/useDeferUntilOnReady/index.mjs +1 -3
  89. package/useDeferUntilOnReady/types.cjs +1 -0
  90. package/useDeferUntilOnReady/types.d.cts +3 -4
  91. package/useDeferUntilOnReady/types.d.mts +3 -4
  92. package/useDeferUntilOnReady/useDeferUntilOnReady.cjs +28 -21
  93. package/useDeferUntilOnReady/useDeferUntilOnReady.d.cts +2 -2
  94. package/useDeferUntilOnReady/useDeferUntilOnReady.d.mts +4 -5
  95. package/useDeferUntilOnReady/useDeferUntilOnReady.mjs +23 -15
  96. package/useDeferUntilReady/index.cjs +4 -4
  97. package/useDeferUntilReady/index.d.cts +1 -1
  98. package/useDeferUntilReady/index.d.mts +1 -1
  99. package/useDeferUntilReady/index.mjs +1 -3
  100. package/useDeferUntilReady/types.cjs +1 -0
  101. package/useDeferUntilReady/types.d.cts +2 -4
  102. package/useDeferUntilReady/types.d.mts +2 -4
  103. package/useDeferUntilReady/useDeferUntilReady.cjs +20 -32
  104. package/useDeferUntilReady/useDeferUntilReady.d.cts +0 -1
  105. package/useDeferUntilReady/useDeferUntilReady.d.mts +2 -4
  106. package/useDeferUntilReady/useDeferUntilReady.mjs +18 -29
  107. package/useDeferUntilRender/index.cjs +4 -4
  108. package/useDeferUntilRender/index.d.cts +1 -1
  109. package/useDeferUntilRender/index.d.mts +1 -1
  110. package/useDeferUntilRender/index.mjs +1 -3
  111. package/useDeferUntilRender/types.cjs +1 -0
  112. package/useDeferUntilRender/types.d.cts +5 -5
  113. package/useDeferUntilRender/types.d.mts +5 -5
  114. package/useDeferUntilRender/useDeferUntilRender.cjs +31 -12
  115. package/useDeferUntilRender/useDeferUntilRender.d.cts +0 -1
  116. package/useDeferUntilRender/useDeferUntilRender.d.mts +2 -4
  117. package/useDeferUntilRender/useDeferUntilRender.mjs +26 -8
  118. package/useDeferUntilResolved/index.cjs +4 -4
  119. package/useDeferUntilResolved/index.d.cts +1 -1
  120. package/useDeferUntilResolved/index.d.mts +1 -1
  121. package/useDeferUntilResolved/index.mjs +1 -3
  122. package/useDeferUntilResolved/types.cjs +1 -0
  123. package/useDeferUntilResolved/types.d.cts +3 -4
  124. package/useDeferUntilResolved/types.d.mts +3 -4
  125. package/useDeferUntilResolved/useDeferUntilResolved.cjs +8 -5
  126. package/useDeferUntilResolved/useDeferUntilResolved.d.cts +0 -1
  127. package/useDeferUntilResolved/useDeferUntilResolved.d.mts +2 -4
  128. package/useDeferUntilResolved/useDeferUntilResolved.mjs +3 -1
  129. package/useDeferUntilTimeout/index.cjs +4 -4
  130. package/useDeferUntilTimeout/index.d.cts +1 -1
  131. package/useDeferUntilTimeout/index.d.mts +1 -1
  132. package/useDeferUntilTimeout/index.mjs +1 -3
  133. package/useDeferUntilTimeout/types.cjs +1 -0
  134. package/useDeferUntilTimeout/types.d.cts +3 -4
  135. package/useDeferUntilTimeout/types.d.mts +3 -4
  136. package/useDeferUntilTimeout/useDeferUntilTimeout.cjs +23 -21
  137. package/useDeferUntilTimeout/useDeferUntilTimeout.d.cts +0 -1
  138. package/useDeferUntilTimeout/useDeferUntilTimeout.d.mts +2 -4
  139. package/useDeferUntilTimeout/useDeferUntilTimeout.mjs +16 -14
  140. package/useDeferUntilTrue/index.cjs +4 -4
  141. package/useDeferUntilTrue/index.d.cts +1 -1
  142. package/useDeferUntilTrue/index.d.mts +1 -1
  143. package/useDeferUntilTrue/index.mjs +1 -3
  144. package/useDeferUntilTrue/types.cjs +1 -0
  145. package/useDeferUntilTrue/types.d.cts +2 -4
  146. package/useDeferUntilTrue/types.d.mts +2 -4
  147. package/useDeferUntilTrue/useDeferUntilTrue.cjs +6 -5
  148. package/useDeferUntilTrue/useDeferUntilTrue.d.cts +0 -1
  149. package/useDeferUntilTrue/useDeferUntilTrue.d.mts +2 -4
  150. package/useDeferUntilWebFontReady/index.cjs +4 -4
  151. package/useDeferUntilWebFontReady/index.d.cts +1 -1
  152. package/useDeferUntilWebFontReady/index.d.mts +1 -1
  153. package/useDeferUntilWebFontReady/index.mjs +1 -3
  154. package/useDeferUntilWebFontReady/types.cjs +1 -0
  155. package/useDeferUntilWebFontReady/types.d.cts +6 -7
  156. package/useDeferUntilWebFontReady/types.d.mts +6 -7
  157. package/useDeferUntilWebFontReady/useDeferUntilWebFontReady.cjs +23 -14
  158. package/useDeferUntilWebFontReady/useDeferUntilWebFontReady.d.cts +0 -1
  159. package/useDeferUntilWebFontReady/useDeferUntilWebFontReady.d.mts +2 -4
  160. package/useDeferUntilWebFontReady/useDeferUntilWebFontReady.mjs +16 -9
  161. package/useDeferUntilScrolled/index.cjs +0 -5
  162. package/useDeferUntilScrolled/index.d.cts +0 -3
  163. package/useDeferUntilScrolled/index.d.mts +0 -3
  164. package/useDeferUntilScrolled/index.mjs +0 -5
  165. package/useDeferUntilScrolled/types.cjs +0 -0
  166. package/useDeferUntilScrolled/types.d.cts +0 -29
  167. package/useDeferUntilScrolled/types.d.mts +0 -29
  168. package/useDeferUntilScrolled/types.mjs +0 -1
  169. package/useDeferUntilScrolled/useDeferUntilScrolled.cjs +0 -94
  170. package/useDeferUntilScrolled/useDeferUntilScrolled.d.cts +0 -14
  171. package/useDeferUntilScrolled/useDeferUntilScrolled.d.mts +0 -15
  172. package/useDeferUntilScrolled/useDeferUntilScrolled.mjs +0 -91
package/README.ja.md CHANGED
@@ -1,319 +1,313 @@
1
- # @fringeworks/react-defer-rendering
2
-
3
- `@fringeworks/react-defer-rendering` は、指定した条件が満たされるまでコンポーネントの描画を遅延させることに特化したニッチなライブラリです。\
4
- タイマー、Promise、ブラウザAPI(`matchMedia`、`IntersectionObserver`等)など、様々な条件に対応したフック群を提供します。
5
-
6
- **[English README is available here](./README.md)**
7
-
8
- ## インストール
9
-
10
- ```bash
11
- npm install @fringeworks/react-defer-rendering
12
- # または
13
- pnpm add @fringeworks/react-defer-rendering
14
- ```
15
-
16
- ## 使い方
17
-
18
- 各フックは、対象ノードの描画状態を表す `state`(`'pending' | 'fallback' | 'ready'`)と、状態に応じて描画すべき `node` を返します。
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
- - 条件を満たしていない間(`pending`)は、`pending` オプションに指定したノードが表示されます
31
- - 条件を満たすと(`ready`)、第一引数に渡した対象ノードに切り替わります
32
- - 一部のフックは失敗時の状態(`fallback`)にも対応しています
33
-
34
- ### 待つ対象が未指定の場合
35
-
36
- 待つ対象を受け取るフックは、第二引数が`null` / `undefined`の場合、待つものがないとみなして即座に`ready`になります。途中で未指定に変わった場合も`ready`に戻り、未指定から指定に変わった場合は改めて`pending`から待ち始めます。\
37
- フックは条件付きで呼び出せないため、コンポーネントの機能として遅延描画を任意にしたい場合は、第二引数に`undefined`を渡すことで無効にできます。
38
-
39
- | フック | 未指定として扱われる第二引数 |
40
- | ----------------------------------------------------------------------------------------------------- | -------------------------------------- |
41
- | `useDeferUntilTimeout` / `useDeferUntilDate` / `useDeferUntilResolved` / `useDeferUntilAsyncComplete` | `null` / `undefined` |
42
- | `useDeferUntilBreakpoint` / `useDeferUntilRender` | `null` / `undefined` / 空文字 |
43
- | `useDeferUntilWebFontReady` | `null` / `undefined` / 空文字 / 空配列 |
44
- | `useDeferUntilIntersected` / `useDeferUntilScrolled` | 参照(ref)自体が`null` / `undefined` |
45
-
46
- `useDeferUntilIntersected` / `useDeferUntilScrolled`では、参照はあるものの`ref.current`が`null`の場合は「要素がまだマウントされていない」とみなし、`pending`のままになります。
47
-
48
- ```tsx
49
- function Heading({ fontFamily, children }: Props) {
50
- // fontFamilyが未指定なら待たずに表示する
51
- const { node } = useDeferUntilWebFontReady(<h1>{children}</h1>, fontFamily, {
52
- pending: null,
53
- });
54
- return node;
55
- }
56
- ```
57
-
58
- なお、`useDeferUntilTrue`の第二引数は条件そのものであり、`null` / `undefined`は条件を満たしていない(`pending`)として扱われます。
59
-
60
- ## 基本のフック
61
-
62
- ### `useDeferUntilReady`
63
-
64
- `state`(`'pending' | 'fallback' | 'ready'`)を直接指定して描画を制御する、最も基本的なフックです。他のフックはすべてこのフックをベースに実装されています。
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
- 真偽値の条件が`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
- ## 時間経過を待つ
88
-
89
- ### `useDeferUntilTimeout`
90
-
91
- 指定の時間(ミリ秒)が経過するまで描画を遅延させます。
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
- 指定の日時になるまで描画を遅延させます。
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
- ## 非同期処理の完了を待つ
116
-
117
- ### `useDeferUntilResolved`
118
-
119
- Promiseが解決(resolve/reject)するまで描画を遅延させます。
120
-
121
- ```tsx
122
- import { useDeferUntilResolved } from '@fringeworks/react-defer-rendering';
123
-
124
- const { node } = useDeferUntilResolved(<MyComponent />, fetchPromise, {
125
- pending: <Spinner />,
126
- fallback: <ErrorMessage />,
127
- });
128
- ```
129
-
130
- ### `useDeferUntilAsyncComplete`
131
-
132
- 非同期関数の実行が完了するまで描画を遅延させます。`asyncFn`はマウント後(effect内)に呼び出され、Promiseの生成自体を管理します。SSR時は呼び出されません。\n`asyncFn`の参照が変わるたびに再実行されるため、`useCallback`等でメモ化してください。
133
-
134
- ```tsx
135
- import { useDeferUntilAsyncComplete } from '@fringeworks/react-defer-rendering';
136
- import { useCallback } from 'react';
137
-
138
- const loadData = useCallback(
139
- () => fetch('/api/data').then((res) => res.json()),
140
- [],
141
- );
142
- const { node } = useDeferUntilAsyncComplete(<MyComponent />, loadData, {
143
- pending: <Spinner />,
144
- });
145
- ```
146
-
147
- ## 値の変化を待つ
148
-
149
- ### `useDeferUntilChange`
150
-
151
- 指定した値が変化するまで描画を遅延させます。値が変化するたびに一時的に`pending`を経由します。
152
-
153
- ```tsx
154
- import { useDeferUntilChange } from '@fringeworks/react-defer-rendering';
155
-
156
- const { node } = useDeferUntilChange(<Toast>{message}</Toast>, message, {
157
- pending: null,
158
- });
159
- ```
160
-
161
- ## 任意のタイミングで手動制御する
162
-
163
- ### `useDeferUntilOnReady`
164
-
165
- 戻り値の`onReady` / `onFallback` / `onPending`を呼び出すことで、任意のタイミングで状態を制御できます。イベントハンドラー等、フック側では検知できない条件に対応する場合に使用します。
166
-
167
- ```tsx
168
- import { useDeferUntilOnReady } from '@fringeworks/react-defer-rendering';
169
-
170
- const { node, onReady } = useDeferUntilOnReady(<Video />, {
171
- pending: <Spinner />,
172
- });
173
-
174
- <video onCanPlay={onReady}>{node}</video>;
175
- ```
176
-
177
- ### `useDeferUntilCallThreshold`
178
-
179
- `useDeferUntilOnReady`と同様ですが、各ハンドラーが指定回数呼ばれて初めて状態が切り替わります。
180
-
181
- ```tsx
182
- import { useDeferUntilCallThreshold } from '@fringeworks/react-defer-rendering';
183
-
184
- const { node, onReady } = useDeferUntilCallThreshold(<Gallery />, {
185
- pending: <Spinner />,
186
- onReadyCount: 3, // onReadyが3回呼ばれたらreadyにする
187
- });
188
- ```
189
-
190
- ## ブラウザの状態を待つ
191
-
192
- > **注意(SSR / RSC):** ここで挙げるフックはブラウザAPIに依存するため、サーバー上では実際の状態を判定できません。詳細は[SSR / RSCでの利用について](#ssr--rscでの利用について)を参照してください。
193
-
194
- ### `useDeferUntilBreakpoint`
195
-
196
- メディアクエリーが一致するまで描画を遅延させます。
197
-
198
- ```tsx
199
- import { useDeferUntilBreakpoint } from '@fringeworks/react-defer-rendering';
200
-
201
- const { node } = useDeferUntilBreakpoint(
202
- <DesktopNav />,
203
- '(min-width: 1024px)',
204
- { pending: <MobileNav /> },
205
- );
206
- ```
207
-
208
- ### `useDeferUntilIntersected`
209
-
210
- 基準となる要素がビューポートに入るまで描画を遅延させます。
211
-
212
- ```tsx
213
- import { useRef } from 'react';
214
- import { useDeferUntilIntersected } from '@fringeworks/react-defer-rendering';
215
-
216
- const elementRef = useRef<HTMLDivElement>(null);
217
- const { node } = useDeferUntilIntersected(<HeavyChart />, elementRef, {
218
- pending: <Placeholder />,
219
- });
220
-
221
- <div ref={elementRef}>{node}</div>;
222
- ```
223
-
224
- ### `useDeferUntilScrolled`
225
-
226
- 基準となる要素がスクロールによって可視範囲に入るまで描画を遅延させます。
227
-
228
- ```tsx
229
- const { node } = useDeferUntilScrolled(<LazyImage />, elementRef, {
230
- pending: <Placeholder />,
231
- rootMargin: 100,
232
- });
233
- ```
234
-
235
- ### `useDeferUntilRender`
236
-
237
- セレクターに一致する要素がDOMに描画されるまで描画を遅延させます。自身の管理外にある要素(サードパーティスクリプトが挿入する要素等)の出現を待つ場合に使用します。
238
-
239
- ```tsx
240
- import { useDeferUntilRender } from '@fringeworks/react-defer-rendering';
241
-
242
- const { node } = useDeferUntilRender(<Overlay />, '#third-party-widget', {
243
- pending: null,
244
- });
245
- ```
246
-
247
- ### `useDeferUntilWebFontReady`
248
-
249
- 指定のWebフォントがすべて利用可能になるまで描画を遅延させます。Webフォントの確認には[`@fringeworks/web-font-observer`](https://www.npmjs.com/package/@fringeworks/web-font-observer)を使用しています。\
250
- 対象は`@font-face`で定義されたWebフォントです。システムフォントはロードの完了を検知できないため、指定するとタイムアウト後に`fallback`になります。
251
-
252
- ```tsx
253
- import { useDeferUntilWebFontReady } from '@fringeworks/react-defer-rendering';
254
-
255
- const { node } = useDeferUntilWebFontReady(
256
- <Heading>Title</Heading>,
257
- 'Noto Sans JP',
258
- {
259
- pending: <Heading style={{ visibility: 'hidden' }}>Title</Heading>,
260
- },
261
- );
262
- ```
263
-
264
- 複数のWebフォントを待つ場合は配列で指定します。太さなどを指定する場合はオブジェクトで指定します。配列内の`null` / `undefined`は無視されるため、条件付きでWebフォントを追加できます。\
265
- 配列やオブジェクトはインラインで記述しても問題ありません(内容が変わらない限り待機はやり直されません)。
266
-
267
- ```tsx
268
- const { node } = useDeferUntilWebFontReady(<Article />, [
269
- { family: 'Noto Sans JP', text: 'あ' },
270
- { family: 'Roboto', weight: 400 },
271
- isBold ? { family: 'Roboto', weight: 700 } : null,
272
- ]);
273
- ```
274
-
275
- | オプション | 型 | デフォルト | 説明 |
276
- | --------------- | ---------------------------------------- | ----------- | ------------------------------------------------------------------------------------------------ |
277
- | `timeout?` | `number` | `3000` | Webフォントが利用可能にならなかった場合に`fallback`とするまでの時間(ミリ秒) |
278
- | `loader?` | `(signal: AbortSignal) => Promise<void>` | - | Webフォントをロードする関数。完了後に確認を開始します。待つWebフォントが変わった時のみ呼ばれます |
279
- | `initialState?` | `'pending' \| 'ready' \| 'fallback'` | `'pending'` | SSR時など、読み込み状態を判定できない環境での初期状態 |
280
-
281
- オブジェクトで指定できる`weight` / `style` / `width` / `text`は`@fringeworks/web-font-observer`と同じです。
282
-
283
- - 1つでも利用可能にならなかった場合は`fallback`になり、残りの待機は中断されます。
284
- - 既にロード済みのWebフォントは待たずに`ready`になります(キャッシュ済みの場合にちらつきません)。
285
- - `fallback`になった後でWebフォントのロードが完了した場合は`ready`になります。`fallback`を維持したい場合は`preserveOnceFallback`を指定してください。
286
- - `@font-face`の定義は遅延対象のノードの外(グローバルなCSSやレイアウト等)に置いてください。遅延対象の中にあると定義が登録されず、タイムアウトまで待つことになります。
287
-
288
- ## 共通オプション
289
-
290
- ### `pending` / `ready` に関するオプション
291
-
292
- すべてのフックで共通のオプションです。
293
-
294
- | オプション | 型 | 説明 |
295
- | -------------------- | ----------- | -------------------------------------------- |
296
- | `pending?` | `ReactNode` | 条件を待っている間に表示するノード |
297
- | `pendingDefer?` | `number` | `pending`を表示するまでの遅延時間(ミリ秒) |
298
- | `readyDefer?` | `number` | 対象ノードを表示するまでの遅延時間(ミリ秒) |
299
- | `preserveOnceReady?` | `boolean` | 一度`ready`になったら、その状態を保持するか |
300
-
301
- ### `fallback` に関するオプション
302
-
303
- `useDeferUntilReady` / `useDeferUntilResolved` / `useDeferUntilAsyncComplete` / `useDeferUntilOnReady` / `useDeferUntilCallThreshold` / `useDeferUntilWebFontReady` は、失敗時の状態として`fallback`にも対応しています。
304
-
305
- | オプション | 型 | 説明 |
306
- | ----------------------- | ----------- | ---------------------------------------------- |
307
- | `fallback?` | `ReactNode` | 失敗時に表示するノード |
308
- | `fallbackDefer?` | `number` | `fallback`を表示するまでの遅延時間(ミリ秒) |
309
- | `preserveOnceFallback?` | `boolean` | 一度`fallback`になったら、その状態を保持するか |
310
-
311
- ## SSR / RSCでの利用について
312
-
313
- このライブラリの各フックには、ビルド時に`'use client'`ディレクティブが付与されています。Next.jsのApp Router等のReact Server Components環境でも、追加の設定なくクライアントコンポーネントの境界として扱われます。
314
-
315
- `useDeferUntilBreakpoint` / `useDeferUntilIntersected` / `useDeferUntilScrolled` / `useDeferUntilRender` はブラウザAPI(`matchMedia`、`IntersectionObserver`、`MutationObserver`等)に依存するため、サーバー上では実際の状態を判定できません。これらのフックは`initialCondition`オプションで、SSR時に使用する初期値を指定できます(未指定の場合は`false`として扱われます)。同様に`useDeferUntilWebFontReady`は`initialState`オプションでSSR時の初期状態を指定できます。
316
-
317
- ## ライセンス
318
-
319
- MIT
1
+ # @fringeworks/react-defer-rendering
2
+
3
+ `@fringeworks/react-defer-rendering` は、指定した条件が満たされるまでコンポーネントの描画を遅延させることに特化した、誰かにとっては便利なライブラリです。\
4
+ タイマー、Promise、ブラウザAPI(`matchMedia`、`IntersectionObserver`等)など、様々な条件に対応したフック群を提供します。
5
+
6
+ **[English README is available here](./README.md)**
7
+
8
+ ## インストール
9
+
10
+ ```bash
11
+ npm install @fringeworks/react-defer-rendering
12
+ # または
13
+ pnpm add @fringeworks/react-defer-rendering
14
+ ```
15
+
16
+ ## 使い方
17
+
18
+ 各フックは、対象ノードの描画状態を表す `state`(`'pending' | 'fallback' | 'ready'`)と、状態に応じて描画すべき `node` を返します。
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
+ - 条件を満たしていない間(`pending`)は、`pending` オプションに指定したノードが表示されます
31
+ - 条件を満たすと(`ready`)、第一引数に渡した対象ノードに切り替わります
32
+ - 一部のフックは失敗時の状態(`fallback`)にも対応しています
33
+
34
+ ### 待つ対象が未指定の場合
35
+
36
+ 待つ対象を受け取るフックは、第二引数が`null` / `undefined`の場合、待つものがないとみなして即座に`ready`になります。途中で未指定に変わった場合も`ready`に戻り、未指定から指定に変わった場合は改めて`pending`から待ち始めます。\
37
+ フックは条件付きで呼び出せないため、コンポーネントの機能として遅延描画を任意にしたい場合は、第二引数に`undefined`を渡すことで無効にできます。
38
+
39
+ | フック | 未指定として扱われる第二引数 |
40
+ | ----------------------------------------------------------------------------------------------------- | -------------------------------------- |
41
+ | `useDeferUntilTimeout` / `useDeferUntilDate` / `useDeferUntilResolved` / `useDeferUntilAsyncComplete` | `null` / `undefined` |
42
+ | `useDeferUntilBreakpoint` / `useDeferUntilRender` | `null` / `undefined` / 空文字 |
43
+ | `useDeferUntilWebFontReady` | `null` / `undefined` / 空文字 / 空配列 |
44
+ | `useDeferUntilIntersected` | 参照(ref)自体が`null` / `undefined` |
45
+
46
+ `useDeferUntilIntersected`では、参照はあるものの`ref.current`が`null`の場合は「要素がまだマウントされていない」とみなし、`pending`のままになります。
47
+
48
+ ```tsx
49
+ function Heading({ fontFamily, children }: Props) {
50
+ // fontFamilyが未指定なら待たずに表示する
51
+ const { node } = useDeferUntilWebFontReady(<h1>{children}</h1>, fontFamily, {
52
+ pending: null,
53
+ });
54
+ return node;
55
+ }
56
+ ```
57
+
58
+ なお、`useDeferUntilTrue`の第二引数は条件そのものであり、`null` / `undefined`は条件を満たしていない(`pending`)として扱われます。
59
+
60
+ ## 基本のフック
61
+
62
+ ### `useDeferUntilReady`
63
+
64
+ `state`(`'pending' | 'fallback' | 'ready'`)を直接指定して描画を制御する、最も基本的なフックです。他のフックはすべてこのフックをベースに実装されています。
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
+ 真偽値の条件が`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
+ ## 時間経過を待つ
88
+
89
+ ### `useDeferUntilTimeout`
90
+
91
+ 指定の時間(ミリ秒)が経過するまで描画を遅延させます。
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
+ 指定の日時になるまで描画を遅延させます。
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
+ ## 非同期処理の完了を待つ
116
+
117
+ ### `useDeferUntilResolved`
118
+
119
+ Promiseが解決(resolve/reject)するまで描画を遅延させます。\
120
+ Promiseの参照が変わるたびに待機をやり直すため、描画のたびに新しいPromiseを作らないでください(コンポーネントの外で作成するか、`useMemo`等でメモ化してください)。
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
+ 非同期関数の実行が完了するまで描画を遅延させます。`asyncFn`はマウント後(effect内)に呼び出され、Promiseの生成自体を管理します。SSR時は呼び出されません。\
134
+ `asyncFn`の参照が変わるたびに再実行されるため、`useCallback`等でメモ化してください。
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
+ ## 値の変化を待つ
150
+
151
+ ### `useDeferUntilChange`
152
+
153
+ 指定した値が変化するまで描画を遅延させます。値が変化するたびに一時的に`pending`を経由します。
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
+ ## 任意のタイミングで手動制御する
164
+
165
+ ### `useDeferUntilOnReady`
166
+
167
+ 戻り値の`onReady` / `onFallback` / `onPending`を呼び出すことで、任意のタイミングで状態を制御できます。イベントハンドラー等、フック側では検知できない条件に対応する場合に使用します。\
168
+ `onReadyDelay` / `onFallbackDelay` / `onPendingDelay`で状態の変更を遅らせた場合も、どのハンドラーを呼んでも待機中の変更は取り消され、最後に呼んだハンドラーの状態になります。
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
+ `useDeferUntilOnReady`と同様ですが、各ハンドラーが指定回数呼ばれて初めて状態が切り替わります。
183
+
184
+ ```tsx
185
+ import { useDeferUntilCallThreshold } from '@fringeworks/react-defer-rendering';
186
+
187
+ const { node, onReady } = useDeferUntilCallThreshold(<Gallery />, {
188
+ pending: <Spinner />,
189
+ onReadyCount: 3, // onReadyが3回呼ばれたらreadyにする
190
+ });
191
+ ```
192
+
193
+ ## ブラウザの状態を待つ
194
+
195
+ > **注意(SSR / RSC):** ここで挙げるフックはブラウザAPIに依存するため、サーバー上では実際の状態を判定できません。詳細は[SSR / RSCでの利用について](#ssr--rscでの利用について)を参照してください。
196
+
197
+ ### `useDeferUntilBreakpoint`
198
+
199
+ メディアクエリーが一致するまで描画を遅延させます。
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
+ 基準となる要素がビューポートに入るまで描画を遅延させます。
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
+ 判定する範囲は`rootMargin`で広げられます。数値はピクセル(上下左右に同じ値)、文字列は`IntersectionObserver`の`rootMargin`の書式(例: `'100px 0px'`、`'10%'`。単位は`px`と`%`のみ)です。要素がどの程度入ったら描画するかは`threshold`(0~1、既定値は`0.1`)で指定します。
228
+
229
+ ### `useDeferUntilRender`
230
+
231
+ セレクターに一致する要素がDOMに描画されるまで描画を遅延させます。自身の管理外にある要素(サードパーティスクリプトが挿入する要素等)の出現を待つ場合に使用します。
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
+ 指定のWebフォントがすべて利用可能になるまで描画を遅延させます。Webフォントの確認には[`@fringeworks/web-font-observer`](https://www.npmjs.com/package/@fringeworks/web-font-observer)を使用しています。\
244
+ 対象は`@font-face`で定義されたWebフォントです。システムフォントはロードの完了を検知できないため、指定するとタイムアウト後に`fallback`になります。
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
+ 複数のWebフォントを待つ場合は配列で指定します。太さなどを指定する場合はオブジェクトで指定します。配列内の`null` / `undefined`は無視されるため、条件付きでWebフォントを追加できます。\
259
+ 配列やオブジェクトはインラインで記述しても問題ありません(内容が変わらない限り待機はやり直されません)。
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
+ | オプション | 型 | デフォルト | 説明 |
270
+ | --------------- | ---------------------------------------- | ----------- | ------------------------------------------------------------------------------------------------ |
271
+ | `timeout?` | `number` | `3000` | Webフォントが利用可能にならなかった場合に`fallback`とするまでの時間(ミリ秒) |
272
+ | `loader?` | `(signal: AbortSignal) => Promise<void>` | - | Webフォントをロードする関数。完了後に確認を開始します。待つWebフォントが変わった時のみ呼ばれます |
273
+ | `initialState?` | `'pending' \| 'ready' \| 'fallback'` | `'pending'` | SSR時など、読み込み状態を判定できない環境での初期状態 |
274
+
275
+ オブジェクトで指定できる`weight` / `style` / `width` / `text`は`@fringeworks/web-font-observer`と同じです。
276
+
277
+ - 1つでも利用可能にならなかった場合は`fallback`になり、残りの待機は中断されます。
278
+ - 既にロード済みのWebフォントは待たずに`ready`になります(キャッシュ済みの場合にちらつきません)。
279
+ - `fallback`になった後でWebフォントのロードが完了した場合は`ready`になります。`fallback`を維持したい場合は`preserveOnceFallback`を指定してください。
280
+ - `@font-face`の定義は遅延対象のノードの外(グローバルなCSSやレイアウト等)に置いてください。遅延対象の中にあると定義が登録されず、タイムアウトまで待つことになります。
281
+
282
+ ## 共通オプション
283
+
284
+ ### `pending` / `ready` に関するオプション
285
+
286
+ すべてのフックで共通のオプションです。
287
+
288
+ | オプション | 型 | 説明 |
289
+ | -------------------- | ----------- | -------------------------------------------- |
290
+ | `pending?` | `ReactNode` | 条件を待っている間に表示するノード |
291
+ | `pendingDefer?` | `number` | `pending`を表示するまでの遅延時間(ミリ秒) |
292
+ | `readyDefer?` | `number` | 対象ノードを表示するまでの遅延時間(ミリ秒) |
293
+ | `preserveOnceReady?` | `boolean` | 一度`ready`になったら、その状態を保持するか |
294
+
295
+ ### `fallback` に関するオプション
296
+
297
+ `useDeferUntilReady` / `useDeferUntilResolved` / `useDeferUntilAsyncComplete` / `useDeferUntilOnReady` / `useDeferUntilCallThreshold` / `useDeferUntilWebFontReady` は、失敗時の状態として`fallback`にも対応しています。
298
+
299
+ | オプション | 型 | 説明 |
300
+ | ----------------------- | ----------- | ---------------------------------------------- |
301
+ | `fallback?` | `ReactNode` | 失敗時に表示するノード |
302
+ | `fallbackDefer?` | `number` | `fallback`を表示するまでの遅延時間(ミリ秒) |
303
+ | `preserveOnceFallback?` | `boolean` | 一度`fallback`になったら、その状態を保持するか |
304
+
305
+ ## SSR / RSCでの利用について
306
+
307
+ このライブラリの各フックには、ビルド時に`'use client'`ディレクティブが付与されています。Next.jsのApp Router等のReact Server Components環境でも、追加の設定なくクライアントコンポーネントの境界として扱われます。
308
+
309
+ `useDeferUntilBreakpoint` / `useDeferUntilIntersected` / `useDeferUntilRender` はブラウザAPI(`matchMedia`、`IntersectionObserver`、`MutationObserver`等)に依存するため、サーバー上では実際の状態を判定できません。これらのフックは`initialCondition`オプションで、SSR時に使用する初期値を指定できます(未指定の場合は`false`として扱われます)。同様に`useDeferUntilWebFontReady`は`initialState`オプションでSSR時の初期状態を指定できます。
310
+
311
+ ## ライセンス
312
+
313
+ MIT