fivem-nui-react 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Mustafa Burak Güneş
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -10,28 +10,46 @@ npm install fivem-nui-react
10
10
 
11
11
  ## Features
12
12
 
13
- - **useNuiEvent** - Listen for NUI messages from Lua
14
- - **fetchNui** - Send requests to Lua client and receive responses
15
- - **useNuiCallback** - Hook for NUI requests with loading/error states and callback
16
- - **useSendNui** - Hook for sending data without expecting a response
17
- - **isEnvBrowser** - Check if running in browser (debug mode)
18
- - **Mock data support** - Test your UI in browser with simulated responses
13
+ ```tsx
14
+ // Listen for NUI messages from FiveM client
15
+ useNuiEvent("eventName", (data) => {});
19
16
 
20
- ## API Reference
17
+ // Send request and get response
18
+ const data = await fetchNui("eventName", { payload });
21
19
 
22
- ### useNuiEvent
20
+ // Hook with loading/error states and callback
21
+ const [fetch, { loading, error }] = useNuiCallback("eventName", (data) => {});
23
22
 
24
- Listen for NUI messages sent from Lua.
23
+ // Send data without expecting response
24
+ const [send, { loading, error }] = useSendNui("eventName");
25
25
 
26
- ```tsx
27
- useNuiEvent<T>(action: string, handler: (data: T) => void, options?: UseNuiEventOptions<T>): void
26
+ // Check if running in browser (debug mode)
27
+ if (isEnvBrowser())
28
28
  ```
29
29
 
30
- **Parameters:**
30
+ ```lua
31
+ -- Send message from FiveM client to UI
32
+ SendNUIMessage({ action = "eventName", data = { key = "value" } })
33
+
34
+ -- Register callback for UI requests
35
+ RegisterNUICallback("eventName", function(data, cb)
36
+ cb({ response = "data" })
37
+ end)
38
+ ```
39
+
40
+ ## API
41
+
42
+ ### useNuiEvent
31
43
 
32
- - `action` - The action name to listen for
33
- - `handler` - Callback function executed when message is received
34
- - `options` - Optional mock data configuration for browser testing
44
+ Listen for NUI messages sent from FiveM client.
45
+
46
+ ```tsx
47
+ useNuiEvent<T>(
48
+ action: string,
49
+ handler: (data: T) => void,
50
+ options?: { mockData?: T; mockDelay?: number }
51
+ ): void
52
+ ```
35
53
 
36
54
  **Example:**
37
55
 
@@ -41,11 +59,6 @@ import { useNuiEvent } from "fivem-nui-react";
41
59
  function App() {
42
60
  const [visible, setVisible] = useState(false);
43
61
 
44
- useNuiEvent<{ show: boolean }>("toggleUI", (data) => {
45
- setVisible(data.show);
46
- });
47
-
48
- // With mock data for browser testing
49
62
  useNuiEvent<{ show: boolean }>("toggleUI", (data) => setVisible(data.show), {
50
63
  mockData: { show: true },
51
64
  mockDelay: 1000,
@@ -55,7 +68,7 @@ function App() {
55
68
  }
56
69
  ```
57
70
 
58
- **Fivem client side:**
71
+ **FiveM Client (Lua):**
59
72
 
60
73
  ```lua
61
74
  SendNUIMessage({
@@ -68,53 +81,53 @@ SendNUIMessage({
68
81
 
69
82
  ### fetchNui
70
83
 
71
- Send a request to the Fivem client and receive a response.
84
+ Send a request to the FiveM client and receive a response.
72
85
 
73
86
  ```tsx
74
- fetchNui<T, D>(eventName: string, data?: D, options?: FetchNuiOptions<T>): Promise<T>
87
+ fetchNui<T, D>(
88
+ eventName: string,
89
+ data?: D,
90
+ options?: { mockData?: T; mockDelay?: number; signal?: AbortSignal }
91
+ ): Promise<T>
75
92
  ```
76
93
 
77
- **Parameters:**
78
-
79
- - `eventName` - The NUI callback event name
80
- - `data` - Data to send to Lua
81
- - `options` - Optional mock data configuration for browser testing
82
-
83
94
  **Example:**
84
95
 
85
96
  ```tsx
86
97
  import { fetchNui } from "fivem-nui-react";
87
98
 
88
- interface PlayerData {
89
- name: string;
90
- level: number;
91
- }
99
+ const player = await fetchNui<PlayerData>(
100
+ "getPlayerData",
101
+ { id: 1 },
102
+ {
103
+ mockData: { name: "John", level: 10 },
104
+ mockDelay: 500,
105
+ },
106
+ );
107
+ ```
92
108
 
93
- async function getPlayer() {
94
- const player = await fetchNui<PlayerData>("getPlayerData", { id: 1 });
95
- console.log(player.name, player.level);
96
- }
109
+ **With AbortController:**
97
110
 
98
- // With mock data for browser testing
99
- async function getPlayerMocked() {
100
- const player = await fetchNui<PlayerData>(
101
- "getPlayerData",
102
- { id: 1 },
103
- {
104
- mockData: { name: "John", level: 10 },
105
- mockDelay: 500,
106
- },
107
- );
108
- console.log(player.name, player.level);
109
- }
111
+ ```tsx
112
+ const controller = new AbortController();
113
+
114
+ const player = await fetchNui<PlayerData>(
115
+ "getPlayerData",
116
+ { id: 1 },
117
+ {
118
+ signal: controller.signal,
119
+ },
120
+ );
121
+
122
+ // Cancel the request
123
+ controller.abort();
110
124
  ```
111
125
 
112
- **Lua side:**
126
+ **FiveM Client (Lua):**
113
127
 
114
128
  ```lua
115
129
  RegisterNUICallback("getPlayerData", function(data, cb)
116
130
  local playerId = data.id
117
- -- Fetch player data...
118
131
  cb({ name = "John", level = 10 })
119
132
  end)
120
133
  ```
@@ -123,48 +136,28 @@ end)
123
136
 
124
137
  ### useNuiCallback
125
138
 
126
- A hook that wraps `fetchNui` with loading and error state management.
139
+ Hook for NUI requests with loading/error states. Calls the callback with response data.
127
140
 
128
141
  ```tsx
129
142
  useNuiCallback<T, D>(
130
143
  eventName: string,
131
144
  callback: (data: T) => void,
132
- options?: UseNuiCallbackOptions<T>
133
- ): [fetchFn: (data?: D) => Promise<T>, state: { loading: boolean, error: Error | null }]
145
+ options?: { mockData?: T; mockDelay?: number }
146
+ ): [(data?: D) => Promise<T>, { loading: boolean; error: Error | null }]
134
147
  ```
135
148
 
136
- **Parameters:**
137
-
138
- - `eventName` - The NUI callback event name
139
- - `callback` - Callback function executed when response is received
140
- - `options` - Optional mock data configuration for browser testing
141
-
142
- **Returns:**
143
-
144
- - `fetchFn` - Function to trigger the request
145
- - `state.loading` - Boolean indicating if request is in progress
146
- - `state.error` - Error object if request failed
147
-
148
149
  **Example:**
149
150
 
150
151
  ```tsx
151
152
  import { useNuiCallback } from "fivem-nui-react";
152
153
 
153
- interface PlayerData {
154
- name: string;
155
- level: number;
156
- }
157
-
158
154
  function PlayerInfo() {
159
155
  const [player, setPlayer] = useState<PlayerData | null>(null);
160
156
 
161
157
  const [fetchPlayer, { loading, error }] = useNuiCallback<PlayerData>(
162
158
  "getPlayerData",
163
159
  (data) => setPlayer(data),
164
- {
165
- mockData: { name: "John", level: 10 },
166
- mockDelay: 500,
167
- },
160
+ { mockData: { name: "John", level: 10 }, mockDelay: 500 },
168
161
  );
169
162
 
170
163
  useEffect(() => {
@@ -173,41 +166,33 @@ function PlayerInfo() {
173
166
 
174
167
  if (loading) return <div>Loading...</div>;
175
168
  if (error) return <div>Error: {error.message}</div>;
176
- if (!player) return null;
177
169
 
178
- return (
179
- <div>
180
- <p>Name: {player.name}</p>
181
- <p>Level: {player.level}</p>
182
- </div>
183
- );
170
+ return <div>{player?.name}</div>;
184
171
  }
185
172
  ```
186
173
 
174
+ **FiveM Client (Lua):**
175
+
176
+ ```lua
177
+ RegisterNUICallback("getPlayerData", function(data, cb)
178
+ local playerId = data.id
179
+ cb({ name = "John", level = 10 })
180
+ end)
181
+ ```
182
+
187
183
  ---
188
184
 
189
185
  ### useSendNui
190
186
 
191
- A hook for sending data to the Lua client without expecting a response.
187
+ Hook for sending data to FiveM client without expecting a response.
192
188
 
193
189
  ```tsx
194
190
  useSendNui<D>(
195
191
  eventName: string,
196
- options?: UseSendNuiOptions
197
- ): [sendFn: (data?: D) => Promise<void>, state: { loading: boolean, error: Error | null }]
192
+ options?: { mockDelay?: number }
193
+ ): [(data?: D) => Promise<void>, { loading: boolean; error: Error | null }]
198
194
  ```
199
195
 
200
- **Parameters:**
201
-
202
- - `eventName` - The NUI callback event name
203
- - `options` - Optional mock delay configuration for browser testing
204
-
205
- **Returns:**
206
-
207
- - `sendFn` - Function to send data
208
- - `state.loading` - Boolean indicating if request is in progress
209
- - `state.error` - Error object if request failed
210
-
211
196
  **Example:**
212
197
 
213
198
  ```tsx
@@ -216,19 +201,18 @@ import { useSendNui } from "fivem-nui-react";
216
201
  function CloseButton() {
217
202
  const [closeUI, { loading }] = useSendNui<{ reason: string }>("closeUI");
218
203
 
219
- const handleClose = () => {
220
- closeUI({ reason: "user_clicked" });
221
- };
222
-
223
204
  return (
224
- <button onClick={handleClose} disabled={loading}>
225
- {loading ? "Closing..." : "Close"}
205
+ <button
206
+ onClick={() => closeUI({ reason: "user_clicked" })}
207
+ disabled={loading}
208
+ >
209
+ Close
226
210
  </button>
227
211
  );
228
212
  }
229
213
  ```
230
214
 
231
- **Lua side:**
215
+ **FiveM Client (Lua):**
232
216
 
233
217
  ```lua
234
218
  RegisterNUICallback("closeUI", function(data, cb)
@@ -243,80 +227,64 @@ end)
243
227
 
244
228
  Check if running in browser (outside FiveM).
245
229
 
246
- ```tsx
247
- isEnvBrowser(): boolean
248
- ```
249
-
250
- **Example:**
251
-
252
230
  ```tsx
253
231
  import { isEnvBrowser } from "fivem-nui-react";
254
232
 
255
233
  if (isEnvBrowser()) {
256
234
  console.log("Running in browser - debug mode");
257
- } else {
258
- console.log("Running in FiveM");
259
235
  }
260
236
  ```
261
237
 
262
238
  ---
263
239
 
264
- ## Browser Testing (Mock Data)
240
+ ## Browser Testing
265
241
 
266
- All hooks and functions support mock data for testing your UI in a regular browser without FiveM.
242
+ All hooks support mock data for testing in browser without FiveM:
267
243
 
268
- When `isEnvBrowser()` returns `true`:
244
+ | Option | Description | Default |
245
+ | ----------- | ------------------------------------------------------ | ------- |
246
+ | `mockData` | Data to return in browser mode | - |
247
+ | `mockDelay` | Delay in ms before returning | 500 |
248
+ | `signal` | AbortSignal for request cancellation (`fetchNui` only) | - |
269
249
 
270
- - `useNuiEvent` will trigger the handler with `mockData` after `mockDelay` milliseconds
271
- - `fetchNui` will return `mockData` after `mockDelay` milliseconds
272
- - `useNuiCallback` will call the callback with `mockData` after `mockDelay` milliseconds
273
- - `useSendNui` will simulate a delay with `mockDelay` milliseconds
250
+ When `isEnvBrowser()` is `true`:
274
251
 
275
- **Options:**
276
-
277
- - `mockData` - The data to return in browser mode
278
- - `mockDelay` - Delay in milliseconds before returning data (default: 500ms)
252
+ - `useNuiEvent` triggers handler with `mockData` after delay
253
+ - `fetchNui` returns `mockData` after delay
254
+ - `useNuiCallback` calls callback with `mockData` after delay
255
+ - `useSendNui` simulates delay only
279
256
 
280
257
  ---
281
258
 
282
- ## TypeScript Support
259
+ ## TypeScript
283
260
 
284
- All functions are fully typed with generics:
261
+ All functions are fully typed:
285
262
 
286
263
  ```tsx
287
- // Define your types
288
- interface ShowUIData {
289
- visible: boolean;
290
- title: string;
264
+ interface PlayerData {
265
+ name: string;
266
+ level: number;
291
267
  }
292
268
 
293
269
  interface PlayerRequest {
294
270
  id: number;
295
271
  }
296
272
 
297
- interface PlayerResponse {
298
- name: string;
299
- level: number;
300
- }
301
-
302
- // Use with type parameters
303
- useNuiEvent<ShowUIData>("showUI", (data) => {
304
- // data is typed as ShowUIData
305
- console.log(data.visible, data.title);
306
- });
307
-
308
- const player = await fetchNui<PlayerResponse, PlayerRequest>("getPlayer", {
273
+ // Typed response
274
+ const player = await fetchNui<PlayerData, PlayerRequest>("getPlayer", {
309
275
  id: 1,
310
276
  });
311
- // player is typed as PlayerResponse
312
277
 
313
- const [fetchPlayer, { loading }] = useNuiCallback<
314
- PlayerResponse,
315
- PlayerRequest
316
- >("getPlayer", (data) => console.log(data.name));
278
+ // Typed callback
279
+ const [fetchPlayer, { loading }] = useNuiCallback<PlayerData, PlayerRequest>(
280
+ "getPlayer",
281
+ (data) => console.log(data.name),
282
+ );
317
283
  ```
318
284
 
319
- ---
285
+ ## Note
286
+
287
+ Most of the code in this project was written by AI and then manually reviewed and edited.
320
288
 
321
289
  ## License
322
290
 
package/dist/index.d.mts CHANGED
@@ -10,6 +10,7 @@ interface UseNuiEventOptions<T = unknown> {
10
10
  interface FetchNuiOptions<T = unknown> {
11
11
  mockData?: T;
12
12
  mockDelay?: number;
13
+ signal?: AbortSignal;
13
14
  }
14
15
  /**
15
16
  * Checks if running in browser debug mode
@@ -30,7 +31,7 @@ declare function useNuiEvent<T = unknown>(action: string, handler: NuiEventHandl
30
31
  * Sends a request to NUI callback (fivem client)
31
32
  * @param eventName - The callback event name
32
33
  * @param data - Data to send
33
- * @param options - Options for mock data in browser mode
34
+ * @param options - Options for mock data and abort signal in browser mode
34
35
  * @returns Promise<T> - Response from fivem client
35
36
  * @example
36
37
  * const result = await fetchNui("getPlayerData", { id: 1 }, {
package/dist/index.d.ts CHANGED
@@ -10,6 +10,7 @@ interface UseNuiEventOptions<T = unknown> {
10
10
  interface FetchNuiOptions<T = unknown> {
11
11
  mockData?: T;
12
12
  mockDelay?: number;
13
+ signal?: AbortSignal;
13
14
  }
14
15
  /**
15
16
  * Checks if running in browser debug mode
@@ -30,7 +31,7 @@ declare function useNuiEvent<T = unknown>(action: string, handler: NuiEventHandl
30
31
  * Sends a request to NUI callback (fivem client)
31
32
  * @param eventName - The callback event name
32
33
  * @param data - Data to send
33
- * @param options - Options for mock data in browser mode
34
+ * @param options - Options for mock data and abort signal in browser mode
34
35
  * @returns Promise<T> - Response from fivem client
35
36
  * @example
36
37
  * const result = await fetchNui("getPlayerData", { id: 1 }, {
package/dist/index.js CHANGED
@@ -66,9 +66,21 @@ async function fetchNui(eventName, data, options) {
66
66
  headers: {
67
67
  "Content-Type": "application/json; charset=UTF-8"
68
68
  },
69
- body: JSON.stringify(data ?? {})
69
+ body: JSON.stringify(data ?? {}),
70
+ signal: options == null ? void 0 : options.signal
70
71
  });
71
- return response.json();
72
+ if (!response.ok) {
73
+ throw new Error(`NUI request failed: ${eventName} (${response.status})`);
74
+ }
75
+ const text = await response.text();
76
+ if (!text || text.length === 0) {
77
+ return null;
78
+ }
79
+ try {
80
+ return JSON.parse(text);
81
+ } catch {
82
+ return null;
83
+ }
72
84
  }
73
85
  function useNuiCallback(eventName, callback, options) {
74
86
  const [state, setState] = (0, import_react.useState)({
@@ -76,20 +88,46 @@ function useNuiCallback(eventName, callback, options) {
76
88
  error: null
77
89
  });
78
90
  const callbackRef = (0, import_react.useRef)(callback);
91
+ const mountedRef = (0, import_react.useRef)(true);
92
+ const abortControllerRef = (0, import_react.useRef)(null);
79
93
  (0, import_react.useEffect)(() => {
80
94
  callbackRef.current = callback;
81
95
  }, [callback]);
96
+ (0, import_react.useEffect)(() => {
97
+ mountedRef.current = true;
98
+ return () => {
99
+ var _a;
100
+ mountedRef.current = false;
101
+ (_a = abortControllerRef.current) == null ? void 0 : _a.abort();
102
+ };
103
+ }, []);
82
104
  const fetch2 = (0, import_react.useCallback)(
83
105
  async (data) => {
84
- setState({ loading: true, error: null });
106
+ var _a;
107
+ (_a = abortControllerRef.current) == null ? void 0 : _a.abort();
108
+ const controller = new AbortController();
109
+ abortControllerRef.current = controller;
110
+ if (mountedRef.current) {
111
+ setState({ loading: true, error: null });
112
+ }
85
113
  try {
86
- const result = await fetchNui(eventName, data, options);
87
- callbackRef.current(result);
88
- setState({ loading: false, error: null });
114
+ const result = await fetchNui(eventName, data, {
115
+ ...options,
116
+ signal: controller.signal
117
+ });
118
+ if (!controller.signal.aborted && mountedRef.current) {
119
+ callbackRef.current(result);
120
+ setState({ loading: false, error: null });
121
+ }
89
122
  return result;
90
123
  } catch (err) {
124
+ if (controller.signal.aborted) {
125
+ throw err;
126
+ }
91
127
  const error = err instanceof Error ? err : new Error(String(err));
92
- setState({ loading: false, error });
128
+ if (mountedRef.current) {
129
+ setState({ loading: false, error });
130
+ }
93
131
  throw error;
94
132
  }
95
133
  },
@@ -102,9 +140,25 @@ function useSendNui(eventName, options) {
102
140
  loading: false,
103
141
  error: null
104
142
  });
143
+ const mountedRef = (0, import_react.useRef)(true);
144
+ const abortControllerRef = (0, import_react.useRef)(null);
145
+ (0, import_react.useEffect)(() => {
146
+ mountedRef.current = true;
147
+ return () => {
148
+ var _a;
149
+ mountedRef.current = false;
150
+ (_a = abortControllerRef.current) == null ? void 0 : _a.abort();
151
+ };
152
+ }, []);
105
153
  const send = (0, import_react.useCallback)(
106
154
  async (data) => {
107
- setState({ loading: true, error: null });
155
+ var _a;
156
+ (_a = abortControllerRef.current) == null ? void 0 : _a.abort();
157
+ const controller = new AbortController();
158
+ abortControllerRef.current = controller;
159
+ if (mountedRef.current) {
160
+ setState({ loading: true, error: null });
161
+ }
108
162
  try {
109
163
  if (isEnvBrowser()) {
110
164
  const delay = (options == null ? void 0 : options.mockDelay) ?? 500;
@@ -115,13 +169,19 @@ function useSendNui(eventName, options) {
115
169
  headers: {
116
170
  "Content-Type": "application/json; charset=UTF-8"
117
171
  },
118
- body: JSON.stringify(data ?? {})
172
+ body: JSON.stringify(data ?? {}),
173
+ signal: controller.signal
119
174
  });
120
175
  }
121
- setState({ loading: false, error: null });
176
+ if (!controller.signal.aborted && mountedRef.current) {
177
+ setState({ loading: false, error: null });
178
+ }
122
179
  } catch (err) {
180
+ if (controller.signal.aborted) return;
123
181
  const error = err instanceof Error ? err : new Error(String(err));
124
- setState({ loading: false, error });
182
+ if (mountedRef.current) {
183
+ setState({ loading: false, error });
184
+ }
125
185
  throw error;
126
186
  }
127
187
  },
package/dist/index.mjs CHANGED
@@ -39,9 +39,21 @@ async function fetchNui(eventName, data, options) {
39
39
  headers: {
40
40
  "Content-Type": "application/json; charset=UTF-8"
41
41
  },
42
- body: JSON.stringify(data ?? {})
42
+ body: JSON.stringify(data ?? {}),
43
+ signal: options == null ? void 0 : options.signal
43
44
  });
44
- return response.json();
45
+ if (!response.ok) {
46
+ throw new Error(`NUI request failed: ${eventName} (${response.status})`);
47
+ }
48
+ const text = await response.text();
49
+ if (!text || text.length === 0) {
50
+ return null;
51
+ }
52
+ try {
53
+ return JSON.parse(text);
54
+ } catch {
55
+ return null;
56
+ }
45
57
  }
46
58
  function useNuiCallback(eventName, callback, options) {
47
59
  const [state, setState] = useState({
@@ -49,20 +61,46 @@ function useNuiCallback(eventName, callback, options) {
49
61
  error: null
50
62
  });
51
63
  const callbackRef = useRef(callback);
64
+ const mountedRef = useRef(true);
65
+ const abortControllerRef = useRef(null);
52
66
  useEffect(() => {
53
67
  callbackRef.current = callback;
54
68
  }, [callback]);
69
+ useEffect(() => {
70
+ mountedRef.current = true;
71
+ return () => {
72
+ var _a;
73
+ mountedRef.current = false;
74
+ (_a = abortControllerRef.current) == null ? void 0 : _a.abort();
75
+ };
76
+ }, []);
55
77
  const fetch2 = useCallback(
56
78
  async (data) => {
57
- setState({ loading: true, error: null });
79
+ var _a;
80
+ (_a = abortControllerRef.current) == null ? void 0 : _a.abort();
81
+ const controller = new AbortController();
82
+ abortControllerRef.current = controller;
83
+ if (mountedRef.current) {
84
+ setState({ loading: true, error: null });
85
+ }
58
86
  try {
59
- const result = await fetchNui(eventName, data, options);
60
- callbackRef.current(result);
61
- setState({ loading: false, error: null });
87
+ const result = await fetchNui(eventName, data, {
88
+ ...options,
89
+ signal: controller.signal
90
+ });
91
+ if (!controller.signal.aborted && mountedRef.current) {
92
+ callbackRef.current(result);
93
+ setState({ loading: false, error: null });
94
+ }
62
95
  return result;
63
96
  } catch (err) {
97
+ if (controller.signal.aborted) {
98
+ throw err;
99
+ }
64
100
  const error = err instanceof Error ? err : new Error(String(err));
65
- setState({ loading: false, error });
101
+ if (mountedRef.current) {
102
+ setState({ loading: false, error });
103
+ }
66
104
  throw error;
67
105
  }
68
106
  },
@@ -75,9 +113,25 @@ function useSendNui(eventName, options) {
75
113
  loading: false,
76
114
  error: null
77
115
  });
116
+ const mountedRef = useRef(true);
117
+ const abortControllerRef = useRef(null);
118
+ useEffect(() => {
119
+ mountedRef.current = true;
120
+ return () => {
121
+ var _a;
122
+ mountedRef.current = false;
123
+ (_a = abortControllerRef.current) == null ? void 0 : _a.abort();
124
+ };
125
+ }, []);
78
126
  const send = useCallback(
79
127
  async (data) => {
80
- setState({ loading: true, error: null });
128
+ var _a;
129
+ (_a = abortControllerRef.current) == null ? void 0 : _a.abort();
130
+ const controller = new AbortController();
131
+ abortControllerRef.current = controller;
132
+ if (mountedRef.current) {
133
+ setState({ loading: true, error: null });
134
+ }
81
135
  try {
82
136
  if (isEnvBrowser()) {
83
137
  const delay = (options == null ? void 0 : options.mockDelay) ?? 500;
@@ -88,13 +142,19 @@ function useSendNui(eventName, options) {
88
142
  headers: {
89
143
  "Content-Type": "application/json; charset=UTF-8"
90
144
  },
91
- body: JSON.stringify(data ?? {})
145
+ body: JSON.stringify(data ?? {}),
146
+ signal: controller.signal
92
147
  });
93
148
  }
94
- setState({ loading: false, error: null });
149
+ if (!controller.signal.aborted && mountedRef.current) {
150
+ setState({ loading: false, error: null });
151
+ }
95
152
  } catch (err) {
153
+ if (controller.signal.aborted) return;
96
154
  const error = err instanceof Error ? err : new Error(String(err));
97
- setState({ loading: false, error });
155
+ if (mountedRef.current) {
156
+ setState({ loading: false, error });
157
+ }
98
158
  throw error;
99
159
  }
100
160
  },
package/package.json CHANGED
@@ -1,7 +1,15 @@
1
1
  {
2
2
  "name": "fivem-nui-react",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "React hooks and utilities for FiveM NUI development",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/TGIANN/fivem-nui-react.git"
8
+ },
9
+ "homepage": "https://github.com/TGIANN/fivem-nui-react#readme",
10
+ "bugs": {
11
+ "url": "https://github.com/TGIANN/fivem-nui-react/issues"
12
+ },
5
13
  "main": "dist/index.js",
6
14
  "module": "dist/index.mjs",
7
15
  "types": "dist/index.d.ts",
@@ -26,7 +34,7 @@
26
34
  "hooks",
27
35
  "cfx"
28
36
  ],
29
- "author": "",
37
+ "author": "TGIANN (tgiann.com)",
30
38
  "license": "MIT",
31
39
  "type": "commonjs",
32
40
  "peerDependencies": {