request-iframe 0.0.2 → 0.0.3

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 (72) hide show
  1. package/README.CN.md +269 -12
  2. package/README.md +266 -11
  3. package/library/__tests__/channel.test.ts +420 -0
  4. package/library/__tests__/debug.test.ts +588 -0
  5. package/library/__tests__/dispatcher.test.ts +481 -0
  6. package/library/__tests__/requestIframe.test.ts +2127 -99
  7. package/library/__tests__/server.test.ts +738 -0
  8. package/library/api/client.d.ts.map +1 -1
  9. package/library/api/client.js +11 -6
  10. package/library/api/server.d.ts +4 -3
  11. package/library/api/server.d.ts.map +1 -1
  12. package/library/api/server.js +25 -7
  13. package/library/constants/index.d.ts +14 -4
  14. package/library/constants/index.d.ts.map +1 -1
  15. package/library/constants/index.js +15 -7
  16. package/library/constants/messages.d.ts +35 -0
  17. package/library/constants/messages.d.ts.map +1 -1
  18. package/library/constants/messages.js +36 -1
  19. package/library/core/client-server.d.ts +101 -0
  20. package/library/core/client-server.d.ts.map +1 -0
  21. package/library/core/client-server.js +266 -0
  22. package/library/core/client.d.ts +22 -6
  23. package/library/core/client.d.ts.map +1 -1
  24. package/library/core/client.js +159 -24
  25. package/library/core/request.d.ts.map +1 -1
  26. package/library/core/response.d.ts +5 -1
  27. package/library/core/response.d.ts.map +1 -1
  28. package/library/core/response.js +85 -70
  29. package/library/core/server-client.d.ts +3 -1
  30. package/library/core/server-client.d.ts.map +1 -1
  31. package/library/core/server-client.js +19 -9
  32. package/library/core/server.d.ts +9 -1
  33. package/library/core/server.d.ts.map +1 -1
  34. package/library/core/server.js +96 -52
  35. package/library/index.d.ts +1 -1
  36. package/library/index.js +2 -2
  37. package/library/interceptors/index.d.ts.map +1 -1
  38. package/library/message/channel.d.ts +3 -1
  39. package/library/message/channel.d.ts.map +1 -1
  40. package/library/message/dispatcher.d.ts +7 -2
  41. package/library/message/dispatcher.d.ts.map +1 -1
  42. package/library/message/dispatcher.js +47 -2
  43. package/library/message/index.d.ts.map +1 -1
  44. package/library/stream/file-stream.d.ts +5 -0
  45. package/library/stream/file-stream.d.ts.map +1 -1
  46. package/library/stream/file-stream.js +41 -12
  47. package/library/stream/index.d.ts.map +1 -1
  48. package/library/stream/readable-stream.d.ts.map +1 -1
  49. package/library/stream/readable-stream.js +32 -30
  50. package/library/stream/types.d.ts +18 -0
  51. package/library/stream/types.d.ts.map +1 -1
  52. package/library/stream/writable-stream.d.ts +1 -0
  53. package/library/stream/writable-stream.d.ts.map +1 -1
  54. package/library/stream/writable-stream.js +7 -2
  55. package/library/types/index.d.ts +80 -28
  56. package/library/types/index.d.ts.map +1 -1
  57. package/library/utils/cache.d.ts +24 -0
  58. package/library/utils/cache.d.ts.map +1 -1
  59. package/library/utils/cache.js +76 -0
  60. package/library/utils/cookie.d.ts.map +1 -1
  61. package/library/utils/debug.d.ts.map +1 -1
  62. package/library/utils/debug.js +382 -20
  63. package/library/utils/index.d.ts +5 -0
  64. package/library/utils/index.d.ts.map +1 -1
  65. package/library/utils/index.js +14 -1
  66. package/library/utils/path-match.d.ts.map +1 -1
  67. package/library/utils/protocol.d.ts.map +1 -1
  68. package/package.json +3 -1
  69. package/react/library/__tests__/index.test.tsx +238 -267
  70. package/react/library/index.d.ts +4 -3
  71. package/react/library/index.d.ts.map +1 -1
  72. package/react/library/index.js +167 -158
package/README.CN.md CHANGED
@@ -34,6 +34,13 @@
34
34
  - [追踪模式](#追踪模式)
35
35
  - [多语言支持](#多语言支持)
36
36
  - [API 参考](#api-参考)
37
+ - [React Hooks](#react-hooks)
38
+ - [useClient](#useclienttargetfnorref-options-deps)
39
+ - [useServer](#useserveroptions-deps)
40
+ - [useServerHandler](#useserverhandlerserver-path-handler-deps)
41
+ - [useServerHandlerMap](#useserverhandlermapserver-map-deps)
42
+ - [完整示例](#完整示例)
43
+ - [最佳实践](#最佳实践)
37
44
  - [错误处理](#错误处理)
38
45
  - [FAQ](#faq)
39
46
  - [开发](#开发)
@@ -167,7 +174,7 @@ request-iframe 采用三阶段超时策略,智能适应不同场景:
167
174
 
168
175
  ```typescript
169
176
  client.send('/api/getData', data, {
170
- ackTimeout: 500, // 阶段1:等待 ACK 的超时时间(默认 500ms
177
+ ackTimeout: 1000, // 阶段1:等待 ACK 的超时时间(默认 1000ms
171
178
  timeout: 5000, // 阶段2:请求超时时间(默认 5s)
172
179
  asyncTimeout: 120000 // 阶段3:异步请求超时时间(默认 120s)
173
180
  });
@@ -207,7 +214,7 @@ client.send('/api/getData', data, {
207
214
 
208
215
  | 阶段 | 超时时间 | 场景 |
209
216
  |------|----------|------|
210
- | ackTimeout | 较短(500ms) | 快速检测 Server 是否在线,避免长时间等待不可达的 iframe |
217
+ | ackTimeout | 较短(1000ms) | 快速检测 Server 是否在线,避免长时间等待不可达的 iframe。从 500ms 增加到 1000ms,以适应性能较差的环境或浏览器繁忙的场景 |
211
218
  | timeout | 中等(5s) | 适用于简单的同步处理,如读取数据、参数校验等 |
212
219
  | asyncTimeout | 较长(120s) | 适用于复杂异步操作,如文件处理、批量操作、第三方 API 调用等 |
213
220
 
@@ -804,7 +811,7 @@ setMessages({
804
811
  | `target` | `HTMLIFrameElement \| Window` | 目标 iframe 元素或 window 对象 |
805
812
  | `options.secretKey` | `string` | 消息隔离标识(可选) |
806
813
  | `options.trace` | `boolean` | 是否开启追踪模式(可选) |
807
- | `options.ackTimeout` | `number` | 全局默认 ACK 确认超时(ms),默认 500 |
814
+ | `options.ackTimeout` | `number` | 全局默认 ACK 确认超时(ms),默认 1000 |
808
815
  | `options.timeout` | `number` | 全局默认请求超时(ms),默认 5000 |
809
816
  | `options.asyncTimeout` | `number` | 全局默认异步超时(ms),默认 120000 |
810
817
 
@@ -837,7 +844,7 @@ await client.send('/api/longTask', {}, {
837
844
  |------|------|------|
838
845
  | `options.secretKey` | `string` | 消息隔离标识(可选) |
839
846
  | `options.trace` | `boolean` | 是否开启追踪模式(可选) |
840
- | `options.ackTimeout` | `number` | 等待客户端确认超时(ms),默认 5000 |
847
+ | `options.ackTimeout` | `number` | 等待客户端确认超时(ms),默认 1000 |
841
848
 
842
849
  **返回值:** `RequestIframeServer`
843
850
 
@@ -853,7 +860,7 @@ await client.send('/api/longTask', {}, {
853
860
  |------|------|------|
854
861
  | `path` | `string` | 请求路径 |
855
862
  | `body` | `object` | 请求数据(可选) |
856
- | `options.ackTimeout` | `number` | ACK 确认超时(ms),默认 500 |
863
+ | `options.ackTimeout` | `number` | ACK 确认超时(ms),默认 1000 |
857
864
  | `options.timeout` | `number` | 请求超时(ms),默认 5000 |
858
865
  | `options.asyncTimeout` | `number` | 异步超时(ms),默认 120000 |
859
866
  | `options.headers` | `object` | 请求 headers(可选) |
@@ -864,16 +871,11 @@ await client.send('/api/longTask', {}, {
864
871
 
865
872
  ```typescript
866
873
  interface Response<T = any> {
867
- data: T; // 响应数据
874
+ data: T; // 响应数据(自动解析的文件流为 File/Blob)
868
875
  status: number; // 状态码
869
876
  statusText: string; // 状态文本
870
877
  requestId: string; // 请求 ID
871
878
  headers?: Record<string, string | string[]>; // 响应 headers(Set-Cookie 为数组)
872
- fileData?: { // 文件数据(如果有)
873
- content: string; // base64 编码内容
874
- mimeType?: string;
875
- fileName?: string;
876
- };
877
879
  stream?: IIframeReadableStream<T>; // 流响应(如果有)
878
880
  }
879
881
  ```
@@ -984,7 +986,262 @@ server.use(['/a', '/b'], (req, res, next) => { ... });
984
986
 
985
987
  销毁 Server 实例,移除所有监听器。
986
988
 
987
- ### ServerRequest 对象
989
+ ---
990
+
991
+ ## React Hooks
992
+
993
+ request-iframe 提供了 React hooks,方便在 React 应用中使用。从 `request-iframe/react` 导入 hooks:
994
+
995
+ ```typescript
996
+ import { useClient, useServer, useServerHandler, useServerHandlerMap } from 'request-iframe/react';
997
+ ```
998
+
999
+ ### useClient(targetFnOrRef, options?, deps?)
1000
+
1001
+ 用于使用 request-iframe client 的 React hook。
1002
+
1003
+ **参数:**
1004
+
1005
+ | 参数 | 类型 | 说明 |
1006
+ |------|------|------|
1007
+ | `targetFnOrRef` | `(() => HTMLIFrameElement \| Window \| null) \| RefObject<HTMLIFrameElement \| Window>` | 返回 iframe 元素或 Window 对象的函数,或 React ref 对象 |
1008
+ | `options` | `RequestIframeClientOptions` | Client 选项(可选) |
1009
+ | `deps` | `readonly unknown[]` | 依赖数组(可选,当依赖变化时重新创建 client) |
1010
+
1011
+ **返回值:** `RequestIframeClient | null`
1012
+
1013
+ **示例:**
1014
+
1015
+ ```tsx
1016
+ import { useClient } from 'request-iframe/react';
1017
+ import { useRef } from 'react';
1018
+
1019
+ const MyComponent = () => {
1020
+ const iframeRef = useRef<HTMLIFrameElement>(null);
1021
+ const client = useClient(iframeRef, { secretKey: 'my-app' });
1022
+
1023
+ const handleClick = async () => {
1024
+ if (client) {
1025
+ const response = await client.send('/api/data', { id: 1 });
1026
+ console.log(response.data);
1027
+ }
1028
+ };
1029
+
1030
+ return (
1031
+ <div>
1032
+ <iframe ref={iframeRef} src="/iframe.html" />
1033
+ <button onClick={handleClick}>发送请求</button>
1034
+ </div>
1035
+ );
1036
+ };
1037
+ ```
1038
+
1039
+ **使用函数而不是 ref:**
1040
+
1041
+ ```tsx
1042
+ const MyComponent = () => {
1043
+ const iframeRef = useRef<HTMLIFrameElement>(null);
1044
+ const client = useClient(() => iframeRef.current, { secretKey: 'my-app' });
1045
+ // ...
1046
+ };
1047
+ ```
1048
+
1049
+ ### useServer(options?, deps?)
1050
+
1051
+ 用于使用 request-iframe server 的 React hook。
1052
+
1053
+ **参数:**
1054
+
1055
+ | 参数 | 类型 | 说明 |
1056
+ |------|------|------|
1057
+ | `options` | `RequestIframeServerOptions` | Server 选项(可选) |
1058
+ | `deps` | `readonly unknown[]` | 依赖数组(可选,当依赖变化时重新创建 server) |
1059
+
1060
+ **返回值:** `RequestIframeServer | null`
1061
+
1062
+ **示例:**
1063
+
1064
+ ```tsx
1065
+ import { useServer } from 'request-iframe/react';
1066
+
1067
+ const MyComponent = () => {
1068
+ const server = useServer({ secretKey: 'my-app' });
1069
+
1070
+ useEffect(() => {
1071
+ if (!server) return;
1072
+
1073
+ const off = server.on('/api/data', (req, res) => {
1074
+ res.send({ data: 'Hello' });
1075
+ });
1076
+
1077
+ return off; // 组件卸载时清理
1078
+ }, [server]);
1079
+
1080
+ return <div>Server 组件</div>;
1081
+ };
1082
+ ```
1083
+
1084
+ ### useServerHandler(server, path, handler, deps?)
1085
+
1086
+ 用于注册单个 server handler 的 React hook,自动处理清理和闭包问题。
1087
+
1088
+ **参数:**
1089
+
1090
+ | 参数 | 类型 | 说明 |
1091
+ |------|------|------|
1092
+ | `server` | `RequestIframeServer \| null` | Server 实例(来自 `useServer`) |
1093
+ | `path` | `string` | 路由路径 |
1094
+ | `handler` | `ServerHandler` | 处理函数 |
1095
+ | `deps` | `readonly unknown[]` | 依赖数组(可选,当依赖变化时重新注册) |
1096
+
1097
+ **示例:**
1098
+
1099
+ ```tsx
1100
+ import { useServer, useServerHandler } from 'request-iframe/react';
1101
+ import { useState } from 'react';
1102
+
1103
+ const MyComponent = () => {
1104
+ const server = useServer();
1105
+ const [userId, setUserId] = useState(1);
1106
+
1107
+ // Handler 自动使用最新的 userId 值
1108
+ useServerHandler(server, '/api/user', (req, res) => {
1109
+ res.send({ userId, data: 'Hello' });
1110
+ }, [userId]); // 当 userId 变化时重新注册
1111
+
1112
+ return <div>Server 组件</div>;
1113
+ };
1114
+ ```
1115
+
1116
+ **关键特性:**
1117
+ - 自动处理闭包问题 - 始终使用依赖项的最新值
1118
+ - 组件卸载或依赖变化时自动取消注册 handler
1119
+ - 无需手动管理 handler 的注册/清理
1120
+
1121
+ ### useServerHandlerMap(server, map, deps?)
1122
+
1123
+ 用于批量注册多个 server handlers 的 React hook,自动处理清理。
1124
+
1125
+ **参数:**
1126
+
1127
+ | 参数 | 类型 | 说明 |
1128
+ |------|------|------|
1129
+ | `server` | `RequestIframeServer \| null` | Server 实例(来自 `useServer`) |
1130
+ | `map` | `Record<string, ServerHandler>` | 路由路径和处理函数的映射 |
1131
+ | `deps` | `readonly unknown[]` | 依赖数组(可选,当依赖变化时重新注册) |
1132
+
1133
+ **示例:**
1134
+
1135
+ ```tsx
1136
+ import { useServer, useServerHandlerMap } from 'request-iframe/react';
1137
+ import { useState } from 'react';
1138
+
1139
+ const MyComponent = () => {
1140
+ const server = useServer();
1141
+ const [userId, setUserId] = useState(1);
1142
+
1143
+ // 一次性注册多个 handlers
1144
+ useServerHandlerMap(server, {
1145
+ '/api/user': (req, res) => {
1146
+ res.send({ userId, data: '用户数据' });
1147
+ },
1148
+ '/api/posts': (req, res) => {
1149
+ res.send({ userId, data: '文章数据' });
1150
+ }
1151
+ }, [userId]); // 当 userId 变化时重新注册所有 handlers
1152
+
1153
+ return <div>Server 组件</div>;
1154
+ };
1155
+ ```
1156
+
1157
+ **关键特性:**
1158
+ - 批量注册多个 handlers
1159
+ - 自动处理闭包问题 - 始终使用依赖项的最新值
1160
+ - 组件卸载或依赖变化时自动取消注册所有 handlers
1161
+ - 高效 - 仅在 map 的键变化时重新注册
1162
+
1163
+ ### 完整示例
1164
+
1165
+ 以下是一个完整的示例,展示如何在真实应用中使用 React hooks:
1166
+
1167
+ ```tsx
1168
+ import { useClient, useServer, useServerHandler } from 'request-iframe/react';
1169
+ import { useRef, useState } from 'react';
1170
+
1171
+ // 父组件(Client)
1172
+ const ParentComponent = () => {
1173
+ const iframeRef = useRef<HTMLIFrameElement>(null);
1174
+ const client = useClient(iframeRef, { secretKey: 'my-app' });
1175
+ const [data, setData] = useState(null);
1176
+
1177
+ const fetchData = async () => {
1178
+ if (!client) return;
1179
+
1180
+ try {
1181
+ const response = await client.send('/api/data', { id: 1 });
1182
+ setData(response.data);
1183
+ } catch (error) {
1184
+ console.error('请求失败:', error);
1185
+ }
1186
+ };
1187
+
1188
+ return (
1189
+ <div>
1190
+ <iframe ref={iframeRef} src="/iframe.html" />
1191
+ <button onClick={fetchData}>获取数据</button>
1192
+ {data && <pre>{JSON.stringify(data, null, 2)}</pre>}
1193
+ </div>
1194
+ );
1195
+ };
1196
+
1197
+ // Iframe 组件(Server)
1198
+ const IframeComponent = () => {
1199
+ const server = useServer({ secretKey: 'my-app' });
1200
+ const [userId, setUserId] = useState(1);
1201
+
1202
+ // 注册 handler,自动清理
1203
+ useServerHandler(server, '/api/data', async (req, res) => {
1204
+ // Handler 始终使用最新的 userId 值
1205
+ const userData = await fetchUserData(userId);
1206
+ res.send(userData);
1207
+ }, [userId]);
1208
+
1209
+ return (
1210
+ <div>
1211
+ <p>用户 ID: {userId}</p>
1212
+ <button onClick={() => setUserId(userId + 1)}>增加</button>
1213
+ </div>
1214
+ );
1215
+ };
1216
+ ```
1217
+
1218
+ ### 最佳实践
1219
+
1220
+ 1. **始终检查 null**:Client 和 server hooks 在初始时或目标不可用时可能返回 `null`:
1221
+ ```tsx
1222
+ const client = useClient(iframeRef);
1223
+ if (!client) return null; // 处理 null 情况
1224
+ ```
1225
+
1226
+ 2. **使用依赖数组**:向 hooks 传递依赖项,确保 handlers 使用最新值:
1227
+ ```tsx
1228
+ useServerHandler(server, '/api/data', (req, res) => {
1229
+ res.send({ userId }); // 始终使用最新的 userId
1230
+ }, [userId]); // 当 userId 变化时重新注册
1231
+ ```
1232
+
1233
+ 3. **自动清理**:Hooks 在组件卸载时自动清理,但你也可以手动取消注册:
1234
+ ```tsx
1235
+ useEffect(() => {
1236
+ if (!server) return;
1237
+ const off = server.on('/api/data', handler);
1238
+ return off; // 手动清理(可选,hooks 会自动处理)
1239
+ }, [server]);
1240
+ ```
1241
+
1242
+ ---
1243
+
1244
+ ## 错误处理
988
1245
 
989
1246
  ```typescript
990
1247
  interface ServerRequest {
package/README.md CHANGED
@@ -34,6 +34,13 @@ Communicate with iframes like sending HTTP requests! A cross-origin iframe commu
34
34
  - [Trace Mode](#trace-mode)
35
35
  - [Internationalization](#internationalization)
36
36
  - [API Reference](#api-reference)
37
+ - [React Hooks](#react-hooks)
38
+ - [useClient](#useclienttargetfnorref-options-deps)
39
+ - [useServer](#useserveroptions-deps)
40
+ - [useServerHandler](#useserverhandlerserver-path-handler-deps)
41
+ - [useServerHandlerMap](#useserverhandlermapserver-map-deps)
42
+ - [Complete Example](#complete-example)
43
+ - [Best Practices](#best-practices)
37
44
  - [Error Handling](#error-handling)
38
45
  - [FAQ](#faq)
39
46
  - [Development](#development)
@@ -251,7 +258,7 @@ request-iframe uses a three-stage timeout strategy to intelligently adapt to dif
251
258
 
252
259
  ```typescript
253
260
  client.send('/api/getData', data, {
254
- ackTimeout: 500, // Stage 1: ACK timeout (default 500ms)
261
+ ackTimeout: 1000, // Stage 1: ACK timeout (default 1000ms)
255
262
  timeout: 5000, // Stage 2: Request timeout (default 5s)
256
263
  asyncTimeout: 120000 // Stage 3: Async request timeout (default 120s)
257
264
  });
@@ -291,7 +298,7 @@ Send REQUEST
291
298
 
292
299
  | Stage | Timeout | Scenario |
293
300
  |-------|---------|----------|
294
- | ackTimeout | Short (500ms) | Quickly detect if Server is online, avoid long waits for unreachable iframes |
301
+ | ackTimeout | Short (1000ms) | Quickly detect if Server is online, avoid long waits for unreachable iframes. Increased from 500ms to accommodate slower environments or busy browsers |
295
302
  | timeout | Medium (5s) | Suitable for simple synchronous processing, like reading data, parameter validation |
296
303
  | asyncTimeout | Long (120s) | Suitable for complex async operations, like file processing, batch operations, third-party API calls |
297
304
 
@@ -712,7 +719,7 @@ Create a Client instance.
712
719
  | `target` | `HTMLIFrameElement \| Window` | Target iframe element or window object |
713
720
  | `options.secretKey` | `string` | Message isolation identifier (optional) |
714
721
  | `options.trace` | `boolean` | Whether to enable trace mode (optional) |
715
- | `options.ackTimeout` | `number` | Global default ACK acknowledgment timeout (ms), default 500 |
722
+ | `options.ackTimeout` | `number` | Global default ACK acknowledgment timeout (ms), default 1000 |
716
723
  | `options.timeout` | `number` | Global default request timeout (ms), default 5000 |
717
724
  | `options.asyncTimeout` | `number` | Global default async timeout (ms), default 120000 |
718
725
 
@@ -728,7 +735,7 @@ Create a Server instance.
728
735
  |-----------|------|-------------|
729
736
  | `options.secretKey` | `string` | Message isolation identifier (optional) |
730
737
  | `options.trace` | `boolean` | Whether to enable trace mode (optional) |
731
- | `options.ackTimeout` | `number` | Wait for client acknowledgment timeout (ms), default 5000 |
738
+ | `options.ackTimeout` | `number` | Wait for client acknowledgment timeout (ms), default 1000 |
732
739
 
733
740
  **Returns:** `RequestIframeServer`
734
741
 
@@ -744,7 +751,7 @@ Send a request.
744
751
  |-----------|------|-------------|
745
752
  | `path` | `string` | Request path |
746
753
  | `body` | `object` | Request data (optional) |
747
- | `options.ackTimeout` | `number` | ACK acknowledgment timeout (ms), default 500 |
754
+ | `options.ackTimeout` | `number` | ACK acknowledgment timeout (ms), default 1000 |
748
755
  | `options.timeout` | `number` | Request timeout (ms), default 5000 |
749
756
  | `options.asyncTimeout` | `number` | Async timeout (ms), default 120000 |
750
757
  | `options.headers` | `object` | Request headers (optional) |
@@ -755,16 +762,11 @@ Send a request.
755
762
 
756
763
  ```typescript
757
764
  interface Response<T = any> {
758
- data: T; // Response data
765
+ data: T; // Response data (File/Blob for auto-resolved file streams)
759
766
  status: number; // Status code
760
767
  statusText: string; // Status text
761
768
  requestId: string; // Request ID
762
769
  headers?: Record<string, string | string[]>; // Response headers (Set-Cookie is array)
763
- fileData?: { // File data (if any)
764
- content: string; // base64-encoded content
765
- mimeType?: string;
766
- fileName?: string;
767
- };
768
770
  stream?: IIframeReadableStream<T>; // Stream response (if any)
769
771
  }
770
772
  ```
@@ -840,6 +842,259 @@ Destroy Server instance, remove all listeners.
840
842
 
841
843
  ---
842
844
 
845
+ ## React Hooks
846
+
847
+ request-iframe provides React hooks for easy integration in React applications. Import hooks from `request-iframe/react`:
848
+
849
+ ```typescript
850
+ import { useClient, useServer, useServerHandler, useServerHandlerMap } from 'request-iframe/react';
851
+ ```
852
+
853
+ ### useClient(targetFnOrRef, options?, deps?)
854
+
855
+ React hook for using request-iframe client.
856
+
857
+ **Parameters:**
858
+
859
+ | Parameter | Type | Description |
860
+ |-----------|------|-------------|
861
+ | `targetFnOrRef` | `(() => HTMLIFrameElement \| Window \| null) \| RefObject<HTMLIFrameElement \| Window>` | Function that returns iframe element or Window object, or a React ref object |
862
+ | `options` | `RequestIframeClientOptions` | Client options (optional) |
863
+ | `deps` | `readonly unknown[]` | Dependency array (optional, for re-creating client when dependencies change) |
864
+
865
+ **Returns:** `RequestIframeClient | null`
866
+
867
+ **Example:**
868
+
869
+ ```tsx
870
+ import { useClient } from 'request-iframe/react';
871
+ import { useRef } from 'react';
872
+
873
+ const MyComponent = () => {
874
+ const iframeRef = useRef<HTMLIFrameElement>(null);
875
+ const client = useClient(iframeRef, { secretKey: 'my-app' });
876
+
877
+ const handleClick = async () => {
878
+ if (client) {
879
+ const response = await client.send('/api/data', { id: 1 });
880
+ console.log(response.data);
881
+ }
882
+ };
883
+
884
+ return (
885
+ <div>
886
+ <iframe ref={iframeRef} src="/iframe.html" />
887
+ <button onClick={handleClick}>Send Request</button>
888
+ </div>
889
+ );
890
+ };
891
+ ```
892
+
893
+ **Using function instead of ref:**
894
+
895
+ ```tsx
896
+ const MyComponent = () => {
897
+ const iframeRef = useRef<HTMLIFrameElement>(null);
898
+ const client = useClient(() => iframeRef.current, { secretKey: 'my-app' });
899
+ // ...
900
+ };
901
+ ```
902
+
903
+ ### useServer(options?, deps?)
904
+
905
+ React hook for using request-iframe server.
906
+
907
+ **Parameters:**
908
+
909
+ | Parameter | Type | Description |
910
+ |-----------|------|-------------|
911
+ | `options` | `RequestIframeServerOptions` | Server options (optional) |
912
+ | `deps` | `readonly unknown[]` | Dependency array (optional, for re-creating server when dependencies change) |
913
+
914
+ **Returns:** `RequestIframeServer | null`
915
+
916
+ **Example:**
917
+
918
+ ```tsx
919
+ import { useServer } from 'request-iframe/react';
920
+
921
+ const MyComponent = () => {
922
+ const server = useServer({ secretKey: 'my-app' });
923
+
924
+ useEffect(() => {
925
+ if (!server) return;
926
+
927
+ const off = server.on('/api/data', (req, res) => {
928
+ res.send({ data: 'Hello' });
929
+ });
930
+
931
+ return off; // Cleanup on unmount
932
+ }, [server]);
933
+
934
+ return <div>Server Component</div>;
935
+ };
936
+ ```
937
+
938
+ ### useServerHandler(server, path, handler, deps?)
939
+
940
+ React hook for registering a single server handler with automatic cleanup and closure handling.
941
+
942
+ **Parameters:**
943
+
944
+ | Parameter | Type | Description |
945
+ |-----------|------|-------------|
946
+ | `server` | `RequestIframeServer \| null` | Server instance (from `useServer`) |
947
+ | `path` | `string` | Route path |
948
+ | `handler` | `ServerHandler` | Handler function |
949
+ | `deps` | `readonly unknown[]` | Dependency array (optional, for re-registering when dependencies change) |
950
+
951
+ **Example:**
952
+
953
+ ```tsx
954
+ import { useServer, useServerHandler } from 'request-iframe/react';
955
+ import { useState } from 'react';
956
+
957
+ const MyComponent = () => {
958
+ const server = useServer();
959
+ const [userId, setUserId] = useState(1);
960
+
961
+ // Handler automatically uses latest userId value
962
+ useServerHandler(server, '/api/user', (req, res) => {
963
+ res.send({ userId, data: 'Hello' });
964
+ }, [userId]); // Re-register when userId changes
965
+
966
+ return <div>Server Component</div>;
967
+ };
968
+ ```
969
+
970
+ **Key Features:**
971
+ - Automatically handles closure issues - always uses latest values from dependencies
972
+ - Automatically unregisters handler on unmount or when dependencies change
973
+ - No need to manually manage handler registration/cleanup
974
+
975
+ ### useServerHandlerMap(server, map, deps?)
976
+
977
+ React hook for registering multiple server handlers at once with automatic cleanup.
978
+
979
+ **Parameters:**
980
+
981
+ | Parameter | Type | Description |
982
+ |-----------|------|-------------|
983
+ | `server` | `RequestIframeServer \| null` | Server instance (from `useServer`) |
984
+ | `map` | `Record<string, ServerHandler>` | Map of route paths and handler functions |
985
+ | `deps` | `readonly unknown[]` | Dependency array (optional, for re-registering when dependencies change) |
986
+
987
+ **Example:**
988
+
989
+ ```tsx
990
+ import { useServer, useServerHandlerMap } from 'request-iframe/react';
991
+ import { useState } from 'react';
992
+
993
+ const MyComponent = () => {
994
+ const server = useServer();
995
+ const [userId, setUserId] = useState(1);
996
+
997
+ // Register multiple handlers at once
998
+ useServerHandlerMap(server, {
999
+ '/api/user': (req, res) => {
1000
+ res.send({ userId, data: 'User data' });
1001
+ },
1002
+ '/api/posts': (req, res) => {
1003
+ res.send({ userId, data: 'Posts data' });
1004
+ }
1005
+ }, [userId]); // Re-register all handlers when userId changes
1006
+
1007
+ return <div>Server Component</div>;
1008
+ };
1009
+ ```
1010
+
1011
+ **Key Features:**
1012
+ - Batch registration of multiple handlers
1013
+ - Automatically handles closure issues - always uses latest values from dependencies
1014
+ - Automatically unregisters all handlers on unmount or when dependencies change
1015
+ - Efficient - only re-registers when map keys change
1016
+
1017
+ ### Complete Example
1018
+
1019
+ Here's a complete example showing how to use React hooks in a real application:
1020
+
1021
+ ```tsx
1022
+ import { useClient, useServer, useServerHandler } from 'request-iframe/react';
1023
+ import { useRef, useState } from 'react';
1024
+
1025
+ // Parent Component (Client)
1026
+ const ParentComponent = () => {
1027
+ const iframeRef = useRef<HTMLIFrameElement>(null);
1028
+ const client = useClient(iframeRef, { secretKey: 'my-app' });
1029
+ const [data, setData] = useState(null);
1030
+
1031
+ const fetchData = async () => {
1032
+ if (!client) return;
1033
+
1034
+ try {
1035
+ const response = await client.send('/api/data', { id: 1 });
1036
+ setData(response.data);
1037
+ } catch (error) {
1038
+ console.error('Request failed:', error);
1039
+ }
1040
+ };
1041
+
1042
+ return (
1043
+ <div>
1044
+ <iframe ref={iframeRef} src="/iframe.html" />
1045
+ <button onClick={fetchData}>Fetch Data</button>
1046
+ {data && <pre>{JSON.stringify(data, null, 2)}</pre>}
1047
+ </div>
1048
+ );
1049
+ };
1050
+
1051
+ // Iframe Component (Server)
1052
+ const IframeComponent = () => {
1053
+ const server = useServer({ secretKey: 'my-app' });
1054
+ const [userId, setUserId] = useState(1);
1055
+
1056
+ // Register handler with automatic cleanup
1057
+ useServerHandler(server, '/api/data', async (req, res) => {
1058
+ // Handler always uses latest userId value
1059
+ const userData = await fetchUserData(userId);
1060
+ res.send(userData);
1061
+ }, [userId]);
1062
+
1063
+ return (
1064
+ <div>
1065
+ <p>User ID: {userId}</p>
1066
+ <button onClick={() => setUserId(userId + 1)}>Increment</button>
1067
+ </div>
1068
+ );
1069
+ };
1070
+ ```
1071
+
1072
+ ### Best Practices
1073
+
1074
+ 1. **Always check for null**: Client and server hooks may return `null` initially or when target is unavailable:
1075
+ ```tsx
1076
+ const client = useClient(iframeRef);
1077
+ if (!client) return null; // Handle null case
1078
+ ```
1079
+
1080
+ 2. **Use dependency arrays**: Pass dependencies to hooks to ensure handlers use latest values:
1081
+ ```tsx
1082
+ useServerHandler(server, '/api/data', (req, res) => {
1083
+ res.send({ userId }); // Always uses latest userId
1084
+ }, [userId]); // Re-register when userId changes
1085
+ ```
1086
+
1087
+ 3. **Cleanup is automatic**: Hooks automatically clean up on unmount, but you can also manually unregister:
1088
+ ```tsx
1089
+ useEffect(() => {
1090
+ if (!server) return;
1091
+ const off = server.on('/api/data', handler);
1092
+ return off; // Manual cleanup (optional, hooks do this automatically)
1093
+ }, [server]);
1094
+ ```
1095
+
1096
+ ---
1097
+
843
1098
  ## Error Handling
844
1099
 
845
1100
  ### Error Codes