@fringeworks/react-utils 0.9.1 → 0.10.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 (67) hide show
  1. package/README.ja.md +123 -10
  2. package/README.md +123 -10
  3. package/_helpers/_useExecutionHandler.cjs +37 -0
  4. package/_helpers/_useExecutionHandler.d.cts +18 -0
  5. package/_helpers/_useExecutionHandler.d.mts +18 -0
  6. package/_helpers/_useExecutionHandler.mjs +36 -0
  7. package/hooks/index.cjs +17 -2
  8. package/hooks/index.d.cts +8 -3
  9. package/hooks/index.d.mts +8 -3
  10. package/hooks/index.mjs +13 -3
  11. package/hooks/useExecutionEffect/index.cjs +5 -0
  12. package/hooks/useExecutionEffect/index.d.cts +2 -0
  13. package/hooks/useExecutionEffect/index.d.mts +2 -0
  14. package/hooks/useExecutionEffect/index.mjs +3 -0
  15. package/hooks/useExecutionEffect/useExecutionEffect.cjs +37 -0
  16. package/hooks/useExecutionEffect/useExecutionEffect.d.cts +27 -0
  17. package/hooks/useExecutionEffect/useExecutionEffect.d.mts +27 -0
  18. package/hooks/useExecutionEffect/useExecutionEffect.mjs +36 -0
  19. package/hooks/useExecutionLayoutEffect/index.cjs +5 -0
  20. package/hooks/useExecutionLayoutEffect/index.d.cts +2 -0
  21. package/hooks/useExecutionLayoutEffect/index.d.mts +2 -0
  22. package/hooks/useExecutionLayoutEffect/index.mjs +3 -0
  23. package/hooks/useExecutionLayoutEffect/useExecutionLayoutEffect.cjs +24 -0
  24. package/hooks/useExecutionLayoutEffect/useExecutionLayoutEffect.d.cts +14 -0
  25. package/hooks/useExecutionLayoutEffect/useExecutionLayoutEffect.d.mts +14 -0
  26. package/hooks/useExecutionLayoutEffect/useExecutionLayoutEffect.mjs +23 -0
  27. package/hooks/useIsHydrated/index.cjs +5 -0
  28. package/hooks/useIsHydrated/index.d.cts +2 -0
  29. package/hooks/useIsHydrated/index.d.mts +2 -0
  30. package/hooks/useIsHydrated/index.mjs +3 -0
  31. package/hooks/useIsHydrated/useIsHydrated.cjs +28 -0
  32. package/hooks/useIsHydrated/useIsHydrated.d.cts +15 -0
  33. package/hooks/useIsHydrated/useIsHydrated.d.mts +15 -0
  34. package/hooks/useIsHydrated/useIsHydrated.mjs +27 -0
  35. package/hooks/useRerender/index.cjs +5 -0
  36. package/hooks/useRerender/index.d.cts +2 -0
  37. package/hooks/useRerender/index.d.mts +2 -0
  38. package/hooks/useRerender/index.mjs +3 -0
  39. package/hooks/useRerender/useRerender.cjs +19 -0
  40. package/hooks/useRerender/useRerender.d.cts +11 -0
  41. package/hooks/useRerender/useRerender.d.mts +11 -0
  42. package/hooks/useRerender/useRerender.mjs +18 -0
  43. package/hooks/useStableCallback/index.cjs +5 -0
  44. package/hooks/useStableCallback/index.d.cts +2 -0
  45. package/hooks/useStableCallback/index.d.mts +2 -0
  46. package/hooks/useStableCallback/index.mjs +3 -0
  47. package/hooks/useStableCallback/useStableCallback.cjs +25 -0
  48. package/hooks/useStableCallback/useStableCallback.d.cts +15 -0
  49. package/hooks/useStableCallback/useStableCallback.d.mts +15 -0
  50. package/hooks/useStableCallback/useStableCallback.mjs +24 -0
  51. package/index.cjs +13 -1
  52. package/index.d.cts +9 -3
  53. package/index.d.mts +9 -3
  54. package/index.mjs +8 -2
  55. package/package.json +61 -1
  56. package/utils/getComponentName/getComponentName.cjs +18 -0
  57. package/utils/getComponentName/getComponentName.d.cts +12 -0
  58. package/utils/getComponentName/getComponentName.d.mts +12 -0
  59. package/utils/getComponentName/getComponentName.mjs +17 -0
  60. package/utils/getComponentName/index.cjs +5 -0
  61. package/utils/getComponentName/index.d.cts +2 -0
  62. package/utils/getComponentName/index.d.mts +2 -0
  63. package/utils/getComponentName/index.mjs +3 -0
  64. package/utils/index.cjs +2 -0
  65. package/utils/index.d.cts +3 -1
  66. package/utils/index.d.mts +3 -1
  67. package/utils/index.mjs +2 -1
package/README.ja.md CHANGED
@@ -31,15 +31,20 @@ import useLatestRef from '@fringeworks/react-utils/hooks/useLatestRef';
31
31
 
32
32
  ### hook
33
33
 
34
- | hook | 説明 |
35
- | ----------------------------------- | ---------------------------------------------------------------------- |
36
- | `useCombinedRefs(...refs)` | 複数の ref にまとめて値を渡す callback ref を返す |
37
- | `useConstant(factory)` | 初回のレンダリングで作った値を、アンマウントされるまで返し続ける |
38
- | `useExecutionAt(options?)` | Execution Request プロパティに渡す値と、実行のきっかけになる関数を返す |
39
- | `useIsMounted()` | マウントされているかどうかを判定する関数を返す |
40
- | `useLatestRef(value)` | 最新の値を保持する参照を返す |
41
- | `usePreviousValue(value, options?)` | 前回の値を返す |
42
- | `useStableLength(arr)` | 配列の要素数がレンダリングをまたいで変わらないことを確認する |
34
+ | hook | 説明 |
35
+ | --------------------------------------------- | ---------------------------------------------------------------------- |
36
+ | `useCombinedRefs(...refs)` | 複数の ref にまとめて値を渡す callback ref を返す |
37
+ | `useConstant(factory)` | 初回のレンダリングで作った値を、アンマウントされるまで返し続ける |
38
+ | `useExecutionAt(options?)` | Execution Request プロパティに渡す値と、実行のきっかけになる関数を返す |
39
+ | `useExecutionEffect(request, callback)` | Execution Request プロパティで要求されたときに、処理を実行する |
40
+ | `useExecutionLayoutEffect(request, callback)` | `useExecutionEffect` の、画面に表示する前に実行する版 |
41
+ | `useIsHydrated()` | ハイドレーションが終わっているかを返す |
42
+ | `useIsMounted()` | マウントされているかどうかを判定する関数を返す |
43
+ | `useLatestRef(value)` | 最新の値を保持する参照を返す |
44
+ | `usePreviousValue(value, options?)` | 前回の値を返す |
45
+ | `useRerender()` | 再レンダリングだけを起こす関数を返す |
46
+ | `useStableCallback(callback)` | 参照が変わらず、最新のコールバックを呼ぶ関数を返す |
47
+ | `useStableLength(arr)` | 配列の要素数がレンダリングをまたいで変わらないことを確認する |
43
48
 
44
49
  ### 関数
45
50
 
@@ -50,6 +55,7 @@ import useLatestRef from '@fringeworks/react-utils/hooks/useLatestRef';
50
55
  | `extendElement(element, overrideProps?, ...children)` | `className` と `style` をマージしてエレメントを複製する |
51
56
  | `forEachContent(children, callback)` | Fragment を透過し、エレメント・文字列・数値を順に処理する |
52
57
  | `forEachElement(children, callback)` | Fragment を透過し、エレメントを順に処理する |
58
+ | `getComponentName(Component)` | HOC の `displayName` などに使う、コンポーネントの名前を返す |
53
59
  | `markAt()` | 呼び出すたびに増加する値を返す |
54
60
  | `setRefCurrent(value, ...refs)` | 複数の ref に値を設定し、クリーンアップ関数を返す |
55
61
  | `transformContent(children, callback)` | Fragment を透過し、エレメント・文字列・数値を変換した配列を返す |
@@ -95,7 +101,7 @@ useExecutionAt<P = void>(
95
101
 
96
102
  命令的な API を持つ部品をラップしたコンポーネントで、プロパティを通じてそのメソッドを実行するための hook です。Execution Request プロパティ(`undoAt` のように `At` で終わるプロパティ)に渡す値と、実行のきっかけになる関数を返します。
97
103
 
98
- - `request.at` は初期状態では `0` で、`execute` を呼ぶたびに増加します。受け取る側は `at` が変わったら処理を実行します。
104
+ - `request.at` は初期状態では `0` で、`execute` を呼ぶたびに増加します。受け取る側は `useExecutionEffect` / `useExecutionLayoutEffect` で処理を実行します。
99
105
  - `execute` の参照は常に同じです。
100
106
 
101
107
  ```tsx
@@ -109,6 +115,69 @@ const [undoAt, undo] = useExecutionAt<string>();
109
115
  | ----------------- | --- | --------------------------------- |
110
116
  | `initialPayload?` | `P` | 初期状態の `request.payload` の値 |
111
117
 
118
+ ### `useExecutionEffect`
119
+
120
+ ```ts
121
+ useExecutionEffect<P = void>(
122
+ request: ExecutionRequest<P> | undefined,
123
+ callback: (payload: P | undefined) => void,
124
+ ): void
125
+ ```
126
+
127
+ `useExecutionAt` と対になる、受け取る側の hook です。Execution Request プロパティで要求されたときに、要求の `payload` を渡して `callback` を実行します。処理は画面に表示した後(`useEffect`)に実行します。
128
+
129
+ - `request.at` が変わったときだけ実行します。`payload` は比べません。
130
+ - マウントより前に出た要求は実行しません。マウントしたときにすでに渡されていた要求も実行しません。
131
+ - 次のレンダリングまでに複数回要求した場合は、最後の要求だけを実行します。
132
+ - `callback` は最新のものを呼びます。`callback` の参照が変わっても実行しません。
133
+ - `request` が `undefined` の場合は、要求なしとして扱います。省略できるプロパティをそのまま渡せます。
134
+
135
+ ```tsx
136
+ type Props = { undoAt?: ExecutionRequest<string> };
137
+
138
+ function WrappedImperativeComponent({ undoAt }: Props) {
139
+ const instance = useConstant(() => createImperativeComponent());
140
+ useExecutionEffect(undoAt, (payload) => {
141
+ instance.undo(payload);
142
+ });
143
+ // ...
144
+ }
145
+ ```
146
+
147
+ | 引数 | 型 | 説明 |
148
+ | ---------- | ----------------------------------- | ------------------------------------------- |
149
+ | `request` | `ExecutionRequest<P> \| undefined` | 受け取った Execution Request プロパティの値 |
150
+ | `callback` | `(payload: P \| undefined) => void` | 要求されたときに実行する処理 |
151
+
152
+ ### `useExecutionLayoutEffect`
153
+
154
+ ```ts
155
+ useExecutionLayoutEffect<P = void>(
156
+ request: ExecutionRequest<P> | undefined,
157
+ callback: (payload: P | undefined) => void,
158
+ ): void
159
+ ```
160
+
161
+ `useExecutionEffect` の、処理を画面に表示する前(`useLayoutEffect`)に実行する版です。スクロールの位置を合わせる、計測し直すなど、結果を画面に表示する前に反映したい処理に使います。要求の扱いと引数は `useExecutionEffect` と同じです。
162
+
163
+ ### `useIsHydrated`
164
+
165
+ ```ts
166
+ useIsHydrated(): boolean
167
+ ```
168
+
169
+ ハイドレーションが終わっているかを返します。クライアントにしか無い値(`window`・`localStorage`・画面の幅など)を、ハイドレーションの不一致を起こさずに描画するために使います。
170
+
171
+ - サーバーの描画とハイドレーション中は `false`、その後は `true` を返します。
172
+ - ハイドレーションの後にマウントしたコンポーネントや、クライアントだけで描画した場合は、最初から `true` を返します。
173
+ - `typeof window` による判定と違い、クライアントでもハイドレーション中は `false` になります。そのため、サーバーの HTML と食い違いません。
174
+
175
+ ```tsx
176
+ const isHydrated = useIsHydrated();
177
+
178
+ <p>{isHydrated ? navigator.userAgent : ''}</p>;
179
+ ```
180
+
112
181
  ### `useIsMounted`
113
182
 
114
183
  ```ts
@@ -150,6 +219,38 @@ usePreviousValue<T = unknown>(
150
219
  | `initialValue?` | `T` | 値が一度も変わっていない間に返す値 |
151
220
  | `isEqual?` | `(oldValue: T, newValue: T) => boolean` | 同じ値とみなす場合に `true` を返す比較関数。未指定の場合は `Object.is` |
152
221
 
222
+ ### `useRerender`
223
+
224
+ ```ts
225
+ useRerender(): () => void
226
+ ```
227
+
228
+ 再レンダリングだけを起こす関数を返します。値を持たずに描画し直したい場合(外部の状態を読み直して表示するときなど)に使います。
229
+
230
+ - 返す関数の参照は常に同じです。
231
+
232
+ ### `useStableCallback`
233
+
234
+ ```ts
235
+ useStableCallback<A extends unknown[], R>(
236
+ callback: (...args: A) => R,
237
+ ): (...args: A) => R
238
+ ```
239
+
240
+ 参照が変わらず、呼ぶと最新の `callback` を実行する関数を返します。子のコンポーネントに渡すイベントハンドラーの参照を変えずに、中身は常に最新にする場合に使います。
241
+
242
+ - 返す関数の参照は常に同じです。
243
+ - 最新の `callback` はコミット時に反映されます。レンダリング中には呼ばないでください(古い `callback` が呼ばれます)。イベントハンドラーやエフェクトの中から呼びます。
244
+ - React 19.2 の `useEffectEvent` はエフェクトの中から呼ぶ専用ですが、この hook が返す関数は子のコンポーネントにも渡せます。
245
+
246
+ ```tsx
247
+ const handleSelect = useStableCallback((id: string) => {
248
+ onSelect(id, filter);
249
+ });
250
+
251
+ <MemoizedList onSelect={handleSelect} />;
252
+ ```
253
+
153
254
  ### `useStableLength`
154
255
 
155
256
  ```ts
@@ -242,6 +343,18 @@ Fragment を透過して `children` を順に処理します。`forEachContent`
242
343
  - 配列や iterable も展開します。
243
344
  - `index` は、Fragment をまたいだ連番です。
244
345
 
346
+ ### `getComponentName`
347
+
348
+ ```ts
349
+ getComponentName(Component: ElementType): string
350
+ ```
351
+
352
+ HOC の `displayName` などに使う、コンポーネントの名前を返します。
353
+
354
+ - 組み込みタグ(`'div'` など)はそのまま返します。
355
+ - `displayName` を関数名より優先します。
356
+ - どちらも無い(空文字の)場合は `Unknown` を返します。
357
+
245
358
  ### `markAt`
246
359
 
247
360
  ```ts
package/README.md CHANGED
@@ -31,15 +31,20 @@ import useLatestRef from '@fringeworks/react-utils/hooks/useLatestRef';
31
31
 
32
32
  ### Hooks
33
33
 
34
- | Hook | Description |
35
- | ----------------------------------- | ----------------------------------------------------------------------------- |
36
- | `useCombinedRefs(...refs)` | Returns a callback ref that passes the value to all the given refs |
37
- | `useConstant(factory)` | Keeps returning the value created on the first render until unmount |
38
- | `useExecutionAt(options?)` | Returns a value for an Execution Request prop and a function that triggers it |
39
- | `useIsMounted()` | Returns a function that tells whether the component is mounted |
40
- | `useLatestRef(value)` | Returns a ref that holds the latest value |
41
- | `usePreviousValue(value, options?)` | Returns the previous value |
42
- | `useStableLength(arr)` | Ensures that the length of an array does not change across renders |
34
+ | Hook | Description |
35
+ | --------------------------------------------- | ----------------------------------------------------------------------------- |
36
+ | `useCombinedRefs(...refs)` | Returns a callback ref that passes the value to all the given refs |
37
+ | `useConstant(factory)` | Keeps returning the value created on the first render until unmount |
38
+ | `useExecutionAt(options?)` | Returns a value for an Execution Request prop and a function that triggers it |
39
+ | `useExecutionEffect(request, callback)` | Runs an operation when it is requested through an Execution Request prop |
40
+ | `useExecutionLayoutEffect(request, callback)` | A version of `useExecutionEffect` that runs before the browser paints |
41
+ | `useIsHydrated()` | Returns whether hydration has finished |
42
+ | `useIsMounted()` | Returns a function that tells whether the component is mounted |
43
+ | `useLatestRef(value)` | Returns a ref that holds the latest value |
44
+ | `usePreviousValue(value, options?)` | Returns the previous value |
45
+ | `useRerender()` | Returns a function that only triggers a re-render |
46
+ | `useStableCallback(callback)` | Returns a function with a stable reference that calls the latest callback |
47
+ | `useStableLength(arr)` | Ensures that the length of an array does not change across renders |
43
48
 
44
49
  ### Functions
45
50
 
@@ -50,6 +55,7 @@ import useLatestRef from '@fringeworks/react-utils/hooks/useLatestRef';
50
55
  | `extendElement(element, overrideProps?, ...children)` | Clones an element, merging `className` and `style` |
51
56
  | `forEachContent(children, callback)` | Iterates over elements, strings, and numbers, looking through Fragments |
52
57
  | `forEachElement(children, callback)` | Iterates over elements, looking through Fragments |
58
+ | `getComponentName(Component)` | Returns the name of a component, for use in an HOC's `displayName` |
53
59
  | `markAt()` | Returns a value that increases on every call |
54
60
  | `setRefCurrent(value, ...refs)` | Sets a value to multiple refs and returns a cleanup function |
55
61
  | `transformContent(children, callback)` | Maps elements, strings, and numbers to an array, looking through Fragments |
@@ -95,7 +101,7 @@ useExecutionAt<P = void>(
95
101
 
96
102
  A hook for components that wrap a part with an imperative API, letting you call its methods through props. It returns a value to pass to an Execution Request prop (a prop ending with `At`, such as `undoAt`) and a function that triggers it.
97
103
 
98
- - `request.at` is `0` initially and increases every time `execute` is called. The receiving side runs the operation when `at` changes.
104
+ - `request.at` is `0` initially and increases every time `execute` is called. The receiving side runs the operation with `useExecutionEffect` / `useExecutionLayoutEffect`.
99
105
  - `execute` always keeps the same reference.
100
106
 
101
107
  ```tsx
@@ -109,6 +115,69 @@ const [undoAt, undo] = useExecutionAt<string>();
109
115
  | ----------------- | ---- | -------------------------------------- |
110
116
  | `initialPayload?` | `P` | The initial value of `request.payload` |
111
117
 
118
+ ### `useExecutionEffect`
119
+
120
+ ```ts
121
+ useExecutionEffect<P = void>(
122
+ request: ExecutionRequest<P> | undefined,
123
+ callback: (payload: P | undefined) => void,
124
+ ): void
125
+ ```
126
+
127
+ The receiving counterpart of `useExecutionAt`. When an operation is requested through an Execution Request prop, it runs `callback` with the request's `payload`. The operation runs after the browser paints (`useEffect`).
128
+
129
+ - It runs only when `request.at` changes. `payload` is not compared.
130
+ - Requests made before mount are not run, including a request that was already passed at mount.
131
+ - If multiple requests are made before the next render, only the last one is run.
132
+ - The latest `callback` is called. Changing the reference of `callback` does not run it.
133
+ - An `undefined` `request` is treated as no request, so an optional prop can be passed as is.
134
+
135
+ ```tsx
136
+ type Props = { undoAt?: ExecutionRequest<string> };
137
+
138
+ function WrappedImperativeComponent({ undoAt }: Props) {
139
+ const instance = useConstant(() => createImperativeComponent());
140
+ useExecutionEffect(undoAt, (payload) => {
141
+ instance.undo(payload);
142
+ });
143
+ // ...
144
+ }
145
+ ```
146
+
147
+ | Parameter | Type | Description |
148
+ | ---------- | ----------------------------------- | ------------------------------------------------ |
149
+ | `request` | `ExecutionRequest<P> \| undefined` | The value of the received Execution Request prop |
150
+ | `callback` | `(payload: P \| undefined) => void` | The operation to run when requested |
151
+
152
+ ### `useExecutionLayoutEffect`
153
+
154
+ ```ts
155
+ useExecutionLayoutEffect<P = void>(
156
+ request: ExecutionRequest<P> | undefined,
157
+ callback: (payload: P | undefined) => void,
158
+ ): void
159
+ ```
160
+
161
+ A version of `useExecutionEffect` that runs the operation before the browser paints (`useLayoutEffect`). Use it for operations whose result should be reflected before painting, such as adjusting the scroll position or measuring again. Requests and parameters are handled the same way as `useExecutionEffect`.
162
+
163
+ ### `useIsHydrated`
164
+
165
+ ```ts
166
+ useIsHydrated(): boolean
167
+ ```
168
+
169
+ Returns whether hydration has finished. Use it to render client-only values (`window`, `localStorage`, the viewport width, and so on) without causing a hydration mismatch.
170
+
171
+ - Returns `false` during server rendering and hydration, and `true` afterwards.
172
+ - Returns `true` from the start in components mounted after hydration and in client-only rendering.
173
+ - Unlike checking `typeof window`, it returns `false` during hydration even on the client, so the output matches the server HTML.
174
+
175
+ ```tsx
176
+ const isHydrated = useIsHydrated();
177
+
178
+ <p>{isHydrated ? navigator.userAgent : ''}</p>;
179
+ ```
180
+
112
181
  ### `useIsMounted`
113
182
 
114
183
  ```ts
@@ -150,6 +219,38 @@ When the value changes, the previous value is updated by a state update during r
150
219
  | `initialValue?` | `T` | The value returned until the value changes for the first time |
151
220
  | `isEqual?` | `(oldValue: T, newValue: T) => boolean` | A comparison that returns `true` for values considered equal. Defaults to `Object.is` |
152
221
 
222
+ ### `useRerender`
223
+
224
+ ```ts
225
+ useRerender(): () => void
226
+ ```
227
+
228
+ Returns a function that only triggers a re-render. Use it when you want to render again without holding a value (for example, to show external state after reading it again).
229
+
230
+ - The returned function always keeps the same reference.
231
+
232
+ ### `useStableCallback`
233
+
234
+ ```ts
235
+ useStableCallback<A extends unknown[], R>(
236
+ callback: (...args: A) => R,
237
+ ): (...args: A) => R
238
+ ```
239
+
240
+ Returns a function whose reference never changes and that calls the latest `callback`. Use it to keep the reference of an event handler passed to child components while always running the latest logic.
241
+
242
+ - The returned function always keeps the same reference.
243
+ - The latest `callback` is applied on commit. Do not call the function during rendering (the previous `callback` would be called). Call it from event handlers or effects.
244
+ - React 19.2's `useEffectEvent` can only be called from effects, while the function returned by this hook can also be passed to child components.
245
+
246
+ ```tsx
247
+ const handleSelect = useStableCallback((id: string) => {
248
+ onSelect(id, filter);
249
+ });
250
+
251
+ <MemoizedList onSelect={handleSelect} />;
252
+ ```
253
+
153
254
  ### `useStableLength`
154
255
 
155
256
  ```ts
@@ -242,6 +343,18 @@ Iterates over `children`, looking through Fragments. `forEachContent` handles el
242
343
  - Arrays and iterables are also expanded.
243
344
  - `index` is a sequential number across Fragments.
244
345
 
346
+ ### `getComponentName`
347
+
348
+ ```ts
349
+ getComponentName(Component: ElementType): string
350
+ ```
351
+
352
+ Returns the name of a component, for use in an HOC's `displayName` and the like.
353
+
354
+ - A built-in tag (such as `'div'`) is returned as is.
355
+ - `displayName` takes precedence over the function name.
356
+ - Returns `Unknown` when neither is available (or both are empty).
357
+
245
358
  ### `markAt`
246
359
 
247
360
  ```ts
@@ -0,0 +1,37 @@
1
+ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: 'Module' } });
2
+ const require_hooks_useConstant_useConstant = require("../hooks/useConstant/useConstant.cjs");
3
+ require("../hooks/useConstant/index.cjs");
4
+ const require_utils_markAt_markAt = require("../utils/markAt/markAt.cjs");
5
+ const require_hooks_useLatestRef_useLatestRef = require("../hooks/useLatestRef/useLatestRef.cjs");
6
+ require("../hooks/useLatestRef/index.cjs");
7
+ let react = require("react");
8
+
9
+ //#region src/_helpers/_useExecutionHandler.ts
10
+ /**
11
+ * Execution Request を受け取り、要求があれば処理を実行する関数を返すフック
12
+ *
13
+ * `useExecutionEffect`・`useExecutionLayoutEffect` に共通の処理。返す関数をエフェクトで呼ぶ。
14
+ *
15
+ * - 初回のレンダリングで `markAt()` の値を基準にし、`at` がそれより大きい要求だけを実行する。
16
+ * `at` は `markAt()` で単調に増えるため、マウントより前に出た要求は実行しない
17
+ * - 一度実行した要求は、エフェクトが再実行されても(StrictMode など)実行しない
18
+ * - コールバックは最新のものを呼ぶ
19
+ *
20
+ * @param request 受け取った Execution Request。undefined の場合は要求なし
21
+ * @param callback 要求があったときに実行する処理
22
+ * @returns エフェクトで呼ぶ関数。参照は request が変わったときだけ変わる
23
+ */
24
+ function useExecutionHandler(request, callback) {
25
+ const callbackRef = require_hooks_useLatestRef_useLatestRef.default(callback);
26
+ const baseline = require_hooks_useConstant_useConstant.default(require_utils_markAt_markAt.default);
27
+ const handledAtRef = (0, react.useRef)(baseline);
28
+ return (0, react.useCallback)(() => {
29
+ if (request && request.at > handledAtRef.current) {
30
+ handledAtRef.current = request.at;
31
+ callbackRef.current(request.payload);
32
+ }
33
+ }, [request, callbackRef]);
34
+ }
35
+
36
+ //#endregion
37
+ exports.default = useExecutionHandler;
@@ -0,0 +1,18 @@
1
+ import { ExecutionRequest } from "../hooks/useExecutionAt/types.cjs";
2
+ //#region src/_helpers/_useExecutionHandler.d.ts
3
+ /**
4
+ * Execution Request を受け取り、要求があれば処理を実行する関数を返すフック
5
+ *
6
+ * `useExecutionEffect`・`useExecutionLayoutEffect` に共通の処理。返す関数をエフェクトで呼ぶ。
7
+ *
8
+ * - 初回のレンダリングで `markAt()` の値を基準にし、`at` がそれより大きい要求だけを実行する。
9
+ * `at` は `markAt()` で単調に増えるため、マウントより前に出た要求は実行しない
10
+ * - 一度実行した要求は、エフェクトが再実行されても(StrictMode など)実行しない
11
+ * - コールバックは最新のものを呼ぶ
12
+ *
13
+ * @param request 受け取った Execution Request。undefined の場合は要求なし
14
+ * @param callback 要求があったときに実行する処理
15
+ * @returns エフェクトで呼ぶ関数。参照は request が変わったときだけ変わる
16
+ */
17
+ declare function useExecutionHandler<P>(request: ExecutionRequest<P> | undefined, callback: (payload: P | undefined) => void): () => void;
18
+ export = useExecutionHandler;
@@ -0,0 +1,18 @@
1
+ import { ExecutionRequest } from "../hooks/useExecutionAt/types.mjs";
2
+ //#region src/_helpers/_useExecutionHandler.d.ts
3
+ /**
4
+ * Execution Request を受け取り、要求があれば処理を実行する関数を返すフック
5
+ *
6
+ * `useExecutionEffect`・`useExecutionLayoutEffect` に共通の処理。返す関数をエフェクトで呼ぶ。
7
+ *
8
+ * - 初回のレンダリングで `markAt()` の値を基準にし、`at` がそれより大きい要求だけを実行する。
9
+ * `at` は `markAt()` で単調に増えるため、マウントより前に出た要求は実行しない
10
+ * - 一度実行した要求は、エフェクトが再実行されても(StrictMode など)実行しない
11
+ * - コールバックは最新のものを呼ぶ
12
+ *
13
+ * @param request 受け取った Execution Request。undefined の場合は要求なし
14
+ * @param callback 要求があったときに実行する処理
15
+ * @returns エフェクトで呼ぶ関数。参照は request が変わったときだけ変わる
16
+ */
17
+ export default function useExecutionHandler<P>(request: ExecutionRequest<P> | undefined, callback: (payload: P | undefined) => void): () => void;
18
+ //#endregion
@@ -0,0 +1,36 @@
1
+ import useConstant from "../hooks/useConstant/useConstant.mjs";
2
+ import "../hooks/useConstant/index.mjs";
3
+ import markAt from "../utils/markAt/markAt.mjs";
4
+ import useLatestRef from "../hooks/useLatestRef/useLatestRef.mjs";
5
+ import "../hooks/useLatestRef/index.mjs";
6
+ import { useCallback, useRef } from "react";
7
+
8
+ //#region src/_helpers/_useExecutionHandler.ts
9
+ /**
10
+ * Execution Request を受け取り、要求があれば処理を実行する関数を返すフック
11
+ *
12
+ * `useExecutionEffect`・`useExecutionLayoutEffect` に共通の処理。返す関数をエフェクトで呼ぶ。
13
+ *
14
+ * - 初回のレンダリングで `markAt()` の値を基準にし、`at` がそれより大きい要求だけを実行する。
15
+ * `at` は `markAt()` で単調に増えるため、マウントより前に出た要求は実行しない
16
+ * - 一度実行した要求は、エフェクトが再実行されても(StrictMode など)実行しない
17
+ * - コールバックは最新のものを呼ぶ
18
+ *
19
+ * @param request 受け取った Execution Request。undefined の場合は要求なし
20
+ * @param callback 要求があったときに実行する処理
21
+ * @returns エフェクトで呼ぶ関数。参照は request が変わったときだけ変わる
22
+ */
23
+ function useExecutionHandler(request, callback) {
24
+ const callbackRef = useLatestRef(callback);
25
+ const baseline = useConstant(markAt);
26
+ const handledAtRef = useRef(baseline);
27
+ return useCallback(() => {
28
+ if (request && request.at > handledAtRef.current) {
29
+ handledAtRef.current = request.at;
30
+ callbackRef.current(request.payload);
31
+ }
32
+ }, [request, callbackRef]);
33
+ }
34
+
35
+ //#endregion
36
+ export { useExecutionHandler as default };
package/hooks/index.cjs CHANGED
@@ -7,17 +7,32 @@ const require_hooks_useConstant_useConstant = require("./useConstant/useConstant
7
7
  require("./useConstant/index.cjs");
8
8
  const require_hooks_useExecutionAt_useExecutionAt = require("./useExecutionAt/useExecutionAt.cjs");
9
9
  require("./useExecutionAt/index.cjs");
10
- const require_hooks_useIsMounted_useIsMounted = require("./useIsMounted/useIsMounted.cjs");
11
- require("./useIsMounted/index.cjs");
12
10
  const require_hooks_useLatestRef_useLatestRef = require("./useLatestRef/useLatestRef.cjs");
13
11
  require("./useLatestRef/index.cjs");
12
+ const require_hooks_useExecutionEffect_useExecutionEffect = require("./useExecutionEffect/useExecutionEffect.cjs");
13
+ require("./useExecutionEffect/index.cjs");
14
+ const require_hooks_useExecutionLayoutEffect_useExecutionLayoutEffect = require("./useExecutionLayoutEffect/useExecutionLayoutEffect.cjs");
15
+ require("./useExecutionLayoutEffect/index.cjs");
16
+ const require_hooks_useIsHydrated_useIsHydrated = require("./useIsHydrated/useIsHydrated.cjs");
17
+ require("./useIsHydrated/index.cjs");
18
+ const require_hooks_useIsMounted_useIsMounted = require("./useIsMounted/useIsMounted.cjs");
19
+ require("./useIsMounted/index.cjs");
14
20
  const require_hooks_usePreviousValue_usePreviousValue = require("./usePreviousValue/usePreviousValue.cjs");
15
21
  require("./usePreviousValue/index.cjs");
22
+ const require_hooks_useRerender_useRerender = require("./useRerender/useRerender.cjs");
23
+ require("./useRerender/index.cjs");
24
+ const require_hooks_useStableCallback_useStableCallback = require("./useStableCallback/useStableCallback.cjs");
25
+ require("./useStableCallback/index.cjs");
16
26
 
17
27
  exports.useCombinedRefs = require_hooks_useCombinedRefs_useCombinedRefs.default;
18
28
  exports.useConstant = require_hooks_useConstant_useConstant.default;
19
29
  exports.useExecutionAt = require_hooks_useExecutionAt_useExecutionAt.default;
30
+ exports.useExecutionEffect = require_hooks_useExecutionEffect_useExecutionEffect.default;
31
+ exports.useExecutionLayoutEffect = require_hooks_useExecutionLayoutEffect_useExecutionLayoutEffect.default;
32
+ exports.useIsHydrated = require_hooks_useIsHydrated_useIsHydrated.default;
20
33
  exports.useIsMounted = require_hooks_useIsMounted_useIsMounted.default;
21
34
  exports.useLatestRef = require_hooks_useLatestRef_useLatestRef.default;
22
35
  exports.usePreviousValue = require_hooks_usePreviousValue_usePreviousValue.default;
36
+ exports.useRerender = require_hooks_useRerender_useRerender.default;
37
+ exports.useStableCallback = require_hooks_useStableCallback_useStableCallback.default;
23
38
  exports.useStableLength = require_hooks_useStableLength_useStableLength.default;
package/hooks/index.d.cts CHANGED
@@ -1,12 +1,17 @@
1
+ import { ExecutionFunction, ExecutionRequest, UseExecutionAtOptions, UseExecutionAtResult } from "./useExecutionAt/types.cjs";
2
+ import useExecutionAt from "./useExecutionAt/useExecutionAt.cjs";
1
3
  import useCombinedRefs from "./useCombinedRefs/useCombinedRefs.cjs";
2
4
  import "./useCombinedRefs/index.cjs";
3
5
  import useConstant from "./useConstant/useConstant.cjs";
4
- import { ExecutionFunction, ExecutionRequest, UseExecutionAtOptions, UseExecutionAtResult } from "./useExecutionAt/types.cjs";
5
- import useExecutionAt from "./useExecutionAt/useExecutionAt.cjs";
6
+ import useExecutionEffect from "./useExecutionEffect/useExecutionEffect.cjs";
7
+ import useExecutionLayoutEffect from "./useExecutionLayoutEffect/useExecutionLayoutEffect.cjs";
8
+ import useIsHydrated from "./useIsHydrated/useIsHydrated.cjs";
6
9
  import useIsMounted from "./useIsMounted/useIsMounted.cjs";
7
10
  import { LatestRef } from "./useLatestRef/types.cjs";
8
11
  import useLatestRef from "./useLatestRef/useLatestRef.cjs";
9
12
  import { UsePreviousValueOptions } from "./usePreviousValue/types.cjs";
10
13
  import usePreviousValue from "./usePreviousValue/usePreviousValue.cjs";
14
+ import useRerender from "./useRerender/useRerender.cjs";
15
+ import useStableCallback from "./useStableCallback/useStableCallback.cjs";
11
16
  import useStableLength from "./useStableLength/useStableLength.cjs";
12
- export { type ExecutionFunction, type ExecutionRequest, type LatestRef, type UseExecutionAtOptions, type UseExecutionAtResult, type UsePreviousValueOptions, useCombinedRefs, useConstant, useExecutionAt, useIsMounted, useLatestRef, usePreviousValue, useStableLength };
17
+ export { type ExecutionFunction, type ExecutionRequest, type LatestRef, type UseExecutionAtOptions, type UseExecutionAtResult, type UsePreviousValueOptions, useCombinedRefs, useConstant, useExecutionAt, useExecutionEffect, useExecutionLayoutEffect, useIsHydrated, useIsMounted, useLatestRef, usePreviousValue, useRerender, useStableCallback, useStableLength };
package/hooks/index.d.mts CHANGED
@@ -1,12 +1,17 @@
1
+ import { ExecutionFunction, ExecutionRequest, UseExecutionAtOptions, UseExecutionAtResult } from "./useExecutionAt/types.mjs";
2
+ import useExecutionAt from "./useExecutionAt/useExecutionAt.mjs";
1
3
  import useCombinedRefs from "./useCombinedRefs/useCombinedRefs.mjs";
2
4
  import "./useCombinedRefs/index.mjs";
3
5
  import useConstant from "./useConstant/useConstant.mjs";
4
- import { ExecutionFunction, ExecutionRequest, UseExecutionAtOptions, UseExecutionAtResult } from "./useExecutionAt/types.mjs";
5
- import useExecutionAt from "./useExecutionAt/useExecutionAt.mjs";
6
+ import useExecutionEffect from "./useExecutionEffect/useExecutionEffect.mjs";
7
+ import useExecutionLayoutEffect from "./useExecutionLayoutEffect/useExecutionLayoutEffect.mjs";
8
+ import useIsHydrated from "./useIsHydrated/useIsHydrated.mjs";
6
9
  import useIsMounted from "./useIsMounted/useIsMounted.mjs";
7
10
  import { LatestRef } from "./useLatestRef/types.mjs";
8
11
  import useLatestRef from "./useLatestRef/useLatestRef.mjs";
9
12
  import { UsePreviousValueOptions } from "./usePreviousValue/types.mjs";
10
13
  import usePreviousValue from "./usePreviousValue/usePreviousValue.mjs";
14
+ import useRerender from "./useRerender/useRerender.mjs";
15
+ import useStableCallback from "./useStableCallback/useStableCallback.mjs";
11
16
  import useStableLength from "./useStableLength/useStableLength.mjs";
12
- export { type ExecutionFunction, type ExecutionRequest, type LatestRef, type UseExecutionAtOptions, type UseExecutionAtResult, type UsePreviousValueOptions, useCombinedRefs, useConstant, useExecutionAt, useIsMounted, useLatestRef, usePreviousValue, useStableLength };
17
+ export { type ExecutionFunction, type ExecutionRequest, type LatestRef, type UseExecutionAtOptions, type UseExecutionAtResult, type UsePreviousValueOptions, useCombinedRefs, useConstant, useExecutionAt, useExecutionEffect, useExecutionLayoutEffect, useIsHydrated, useIsMounted, useLatestRef, usePreviousValue, useRerender, useStableCallback, useStableLength };
package/hooks/index.mjs CHANGED
@@ -6,11 +6,21 @@ import useConstant from "./useConstant/useConstant.mjs";
6
6
  import "./useConstant/index.mjs";
7
7
  import useExecutionAt from "./useExecutionAt/useExecutionAt.mjs";
8
8
  import "./useExecutionAt/index.mjs";
9
- import useIsMounted from "./useIsMounted/useIsMounted.mjs";
10
- import "./useIsMounted/index.mjs";
11
9
  import useLatestRef from "./useLatestRef/useLatestRef.mjs";
12
10
  import "./useLatestRef/index.mjs";
11
+ import useExecutionEffect from "./useExecutionEffect/useExecutionEffect.mjs";
12
+ import "./useExecutionEffect/index.mjs";
13
+ import useExecutionLayoutEffect from "./useExecutionLayoutEffect/useExecutionLayoutEffect.mjs";
14
+ import "./useExecutionLayoutEffect/index.mjs";
15
+ import useIsHydrated from "./useIsHydrated/useIsHydrated.mjs";
16
+ import "./useIsHydrated/index.mjs";
17
+ import useIsMounted from "./useIsMounted/useIsMounted.mjs";
18
+ import "./useIsMounted/index.mjs";
13
19
  import usePreviousValue from "./usePreviousValue/usePreviousValue.mjs";
14
20
  import "./usePreviousValue/index.mjs";
21
+ import useRerender from "./useRerender/useRerender.mjs";
22
+ import "./useRerender/index.mjs";
23
+ import useStableCallback from "./useStableCallback/useStableCallback.mjs";
24
+ import "./useStableCallback/index.mjs";
15
25
 
16
- export { useCombinedRefs, useConstant, useExecutionAt, useIsMounted, useLatestRef, usePreviousValue, useStableLength };
26
+ export { useCombinedRefs, useConstant, useExecutionAt, useExecutionEffect, useExecutionLayoutEffect, useIsHydrated, useIsMounted, useLatestRef, usePreviousValue, useRerender, useStableCallback, useStableLength };
@@ -0,0 +1,5 @@
1
+ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: 'Module' } });
2
+ const require_hooks_useExecutionEffect_useExecutionEffect = require("./useExecutionEffect.cjs");
3
+
4
+ exports.default = require_hooks_useExecutionEffect_useExecutionEffect.default;
5
+ exports.useExecutionEffect = require_hooks_useExecutionEffect_useExecutionEffect.default;
@@ -0,0 +1,2 @@
1
+ import useExecutionEffect from "./useExecutionEffect.cjs";
2
+ export { useExecutionEffect as default, useExecutionEffect };
@@ -0,0 +1,2 @@
1
+ import useExecutionEffect from "./useExecutionEffect.mjs";
2
+ export { useExecutionEffect as default, useExecutionEffect };
@@ -0,0 +1,3 @@
1
+ import useExecutionEffect from "./useExecutionEffect.mjs";
2
+
3
+ export { useExecutionEffect as default, useExecutionEffect };
@@ -0,0 +1,37 @@
1
+ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: 'Module' } });
2
+ const require__helpers__useExecutionHandler = require("../../_helpers/_useExecutionHandler.cjs");
3
+ let react = require("react");
4
+
5
+ //#region src/hooks/useExecutionEffect/useExecutionEffect.ts
6
+ /**
7
+ * Execution Request プロパティ(xxxAt)を受け取り、要求があったときに処理を実行するフック
8
+ *
9
+ * `useExecutionAt` と対になる、受け取る側のフック。
10
+ * 命令的なコンポーネントをラップした React のコンポーネントで、プロパティを通じてメソッドを実行する。
11
+ * 処理は画面に表示した後(`useEffect`)に実行する。表示する前に実行する場合は `useExecutionLayoutEffect` を使う。
12
+ *
13
+ * - `at` が変わったときだけ実行する(`payload` は比べない)
14
+ * - マウントより前に出た要求(マウントしたときにすでに渡されていた要求を含む)は実行しない
15
+ * - 次のレンダリングまでに複数回要求した場合は、最後の要求だけを実行する
16
+ * - コールバックは最新のものを呼ぶ。参照が変わっても実行しない
17
+ *
18
+ * @param request 受け取った Execution Request。undefined の場合は要求なし
19
+ * @param callback 要求があったときに実行する処理。要求の payload を受け取る
20
+ * @example
21
+ * // 呼び出す側
22
+ * const [undoAt, undo] = useExecutionAt<string>();
23
+ * <WrappedImperativeComponent undoAt={undoAt} />
24
+ * // 受け取る側(WrappedImperativeComponent の中)
25
+ * useExecutionEffect(props.undoAt, (payload) => {
26
+ * instance.undo(payload);
27
+ * });
28
+ */
29
+ function useExecutionEffect(request, callback) {
30
+ const handle = require__helpers__useExecutionHandler.default(request, callback);
31
+ (0, react.useEffect)(() => {
32
+ handle();
33
+ }, [handle]);
34
+ }
35
+
36
+ //#endregion
37
+ exports.default = useExecutionEffect;