@usefy/use-unmount 0.0.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +143 -0
- package/dist/index.d.mts +98 -0
- package/dist/index.d.ts +98 -0
- package/dist/index.js +50 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +23 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +59 -0
package/README.md
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# @usefy/use-unmount
|
|
2
|
+
|
|
3
|
+
A React hook that executes a callback when the component unmounts.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Closure Freshness**: Callback always has access to the latest state/props values
|
|
8
|
+
- **Error Handling**: Errors in callback are caught and don't break unmount
|
|
9
|
+
- **Conditional Execution**: Enable/disable cleanup via `enabled` option
|
|
10
|
+
- **SSR Compatible**: Works safely with server-side rendering
|
|
11
|
+
- **TypeScript Support**: Full type definitions included
|
|
12
|
+
- **Zero Dependencies**: Only peer dependency on React
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install @usefy/use-unmount
|
|
18
|
+
# or
|
|
19
|
+
pnpm add @usefy/use-unmount
|
|
20
|
+
# or
|
|
21
|
+
yarn add @usefy/use-unmount
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
### Basic Usage
|
|
27
|
+
|
|
28
|
+
```tsx
|
|
29
|
+
import { useUnmount } from "@usefy/use-unmount";
|
|
30
|
+
|
|
31
|
+
function MyComponent() {
|
|
32
|
+
useUnmount(() => {
|
|
33
|
+
console.log("Component unmounted");
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
return <div>Hello</div>;
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### With Latest State Access
|
|
41
|
+
|
|
42
|
+
```tsx
|
|
43
|
+
import { useState } from "react";
|
|
44
|
+
import { useUnmount } from "@usefy/use-unmount";
|
|
45
|
+
|
|
46
|
+
function FormComponent() {
|
|
47
|
+
const [formData, setFormData] = useState({});
|
|
48
|
+
|
|
49
|
+
useUnmount(() => {
|
|
50
|
+
// formData will have the latest value at unmount time
|
|
51
|
+
saveToLocalStorage(formData);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
return <form>...</form>;
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Conditional Cleanup
|
|
59
|
+
|
|
60
|
+
```tsx
|
|
61
|
+
import { useUnmount } from "@usefy/use-unmount";
|
|
62
|
+
|
|
63
|
+
function TrackingComponent({ trackingEnabled }) {
|
|
64
|
+
useUnmount(
|
|
65
|
+
() => {
|
|
66
|
+
sendAnalyticsEvent("component_unmounted");
|
|
67
|
+
},
|
|
68
|
+
{ enabled: trackingEnabled }
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
return <div>Tracked content</div>;
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Resource Cleanup
|
|
76
|
+
|
|
77
|
+
```tsx
|
|
78
|
+
import { useEffect, useRef } from "react";
|
|
79
|
+
import { useUnmount } from "@usefy/use-unmount";
|
|
80
|
+
|
|
81
|
+
function WebSocketComponent() {
|
|
82
|
+
const wsRef = useRef<WebSocket | null>(null);
|
|
83
|
+
|
|
84
|
+
useEffect(() => {
|
|
85
|
+
wsRef.current = new WebSocket("wss://example.com");
|
|
86
|
+
}, []);
|
|
87
|
+
|
|
88
|
+
useUnmount(() => {
|
|
89
|
+
wsRef.current?.close();
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
return <div>Connected</div>;
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## API
|
|
97
|
+
|
|
98
|
+
### `useUnmount(callback, options?)`
|
|
99
|
+
|
|
100
|
+
#### Parameters
|
|
101
|
+
|
|
102
|
+
| Name | Type | Description |
|
|
103
|
+
|------|------|-------------|
|
|
104
|
+
| `callback` | `() => void` | Function to execute when component unmounts |
|
|
105
|
+
| `options` | `UseUnmountOptions` | Optional configuration |
|
|
106
|
+
|
|
107
|
+
#### Options
|
|
108
|
+
|
|
109
|
+
| Name | Type | Default | Description |
|
|
110
|
+
|------|------|---------|-------------|
|
|
111
|
+
| `enabled` | `boolean` | `true` | Whether to execute callback on unmount |
|
|
112
|
+
|
|
113
|
+
## React StrictMode
|
|
114
|
+
|
|
115
|
+
In development with React StrictMode, components are intentionally mounted, unmounted, and remounted to detect side effects. This means the unmount callback may be called multiple times during development. This is expected behavior.
|
|
116
|
+
|
|
117
|
+
## Error Handling
|
|
118
|
+
|
|
119
|
+
If the callback throws an error, it will be caught and logged to the console. This prevents unmount errors from breaking the entire component tree unmount process.
|
|
120
|
+
|
|
121
|
+
## When to Use
|
|
122
|
+
|
|
123
|
+
Use `useUnmount` when you need to:
|
|
124
|
+
|
|
125
|
+
- Save data before component removal
|
|
126
|
+
- Send analytics events on component exit
|
|
127
|
+
- Clean up resources that aren't managed by useEffect
|
|
128
|
+
- Log component lifecycle events
|
|
129
|
+
- Perform final state snapshots
|
|
130
|
+
|
|
131
|
+
## When NOT to Use
|
|
132
|
+
|
|
133
|
+
Consider using `useEffect` cleanup instead when:
|
|
134
|
+
|
|
135
|
+
- Cleaning up subscriptions (use `useEffect` return function)
|
|
136
|
+
- Removing event listeners (use `useEventListener` hook)
|
|
137
|
+
- Canceling requests (use abort controllers in `useEffect`)
|
|
138
|
+
|
|
139
|
+
The key difference is that `useUnmount` guarantees access to the latest values, while `useEffect` cleanup captures values at effect creation time.
|
|
140
|
+
|
|
141
|
+
## License
|
|
142
|
+
|
|
143
|
+
MIT
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Options for useUnmount hook
|
|
3
|
+
*/
|
|
4
|
+
interface UseUnmountOptions {
|
|
5
|
+
/**
|
|
6
|
+
* Whether the unmount callback is enabled.
|
|
7
|
+
* When false, the callback will not be executed on unmount.
|
|
8
|
+
* @default true
|
|
9
|
+
*/
|
|
10
|
+
enabled?: boolean;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Executes a callback function when the component unmounts.
|
|
14
|
+
* The callback always has access to the latest values (closure freshness).
|
|
15
|
+
*
|
|
16
|
+
* Features:
|
|
17
|
+
* - Closure freshness: callback always sees latest state/props
|
|
18
|
+
* - Error handling: errors in callback don't break unmount
|
|
19
|
+
* - Conditional execution via `enabled` option
|
|
20
|
+
* - SSR compatible
|
|
21
|
+
* - TypeScript support
|
|
22
|
+
*
|
|
23
|
+
* @param callback - The function to execute on unmount
|
|
24
|
+
* @param options - Configuration options
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```tsx
|
|
28
|
+
* // Basic usage
|
|
29
|
+
* function MyComponent() {
|
|
30
|
+
* useUnmount(() => {
|
|
31
|
+
* console.log("Component unmounted");
|
|
32
|
+
* });
|
|
33
|
+
*
|
|
34
|
+
* return <div>Hello</div>;
|
|
35
|
+
* }
|
|
36
|
+
* ```
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```tsx
|
|
40
|
+
* // With latest state access
|
|
41
|
+
* function FormComponent() {
|
|
42
|
+
* const [formData, setFormData] = useState({});
|
|
43
|
+
*
|
|
44
|
+
* useUnmount(() => {
|
|
45
|
+
* // formData will have the latest value at unmount time
|
|
46
|
+
* saveToLocalStorage(formData);
|
|
47
|
+
* });
|
|
48
|
+
*
|
|
49
|
+
* return <form>...</form>;
|
|
50
|
+
* }
|
|
51
|
+
* ```
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```tsx
|
|
55
|
+
* // Conditional cleanup
|
|
56
|
+
* function TrackingComponent({ trackingEnabled }) {
|
|
57
|
+
* useUnmount(
|
|
58
|
+
* () => {
|
|
59
|
+
* sendAnalyticsEvent("component_unmounted");
|
|
60
|
+
* },
|
|
61
|
+
* { enabled: trackingEnabled }
|
|
62
|
+
* );
|
|
63
|
+
*
|
|
64
|
+
* return <div>Tracked content</div>;
|
|
65
|
+
* }
|
|
66
|
+
* ```
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* ```tsx
|
|
70
|
+
* // Resource cleanup
|
|
71
|
+
* function WebSocketComponent() {
|
|
72
|
+
* const wsRef = useRef<WebSocket | null>(null);
|
|
73
|
+
*
|
|
74
|
+
* useEffect(() => {
|
|
75
|
+
* wsRef.current = new WebSocket("wss://example.com");
|
|
76
|
+
* }, []);
|
|
77
|
+
*
|
|
78
|
+
* useUnmount(() => {
|
|
79
|
+
* wsRef.current?.close();
|
|
80
|
+
* });
|
|
81
|
+
*
|
|
82
|
+
* return <div>Connected</div>;
|
|
83
|
+
* }
|
|
84
|
+
* ```
|
|
85
|
+
*
|
|
86
|
+
* @remarks
|
|
87
|
+
* **React StrictMode**: In development with StrictMode, React intentionally
|
|
88
|
+
* mounts, unmounts, and remounts components to detect side effects. This means
|
|
89
|
+
* the unmount callback may be called multiple times during development.
|
|
90
|
+
* This is expected behavior and helps identify issues with cleanup logic.
|
|
91
|
+
*
|
|
92
|
+
* **Error Handling**: If the callback throws an error, it will be caught and
|
|
93
|
+
* logged to the console. This prevents unmount errors from breaking
|
|
94
|
+
* the entire component tree unmount process.
|
|
95
|
+
*/
|
|
96
|
+
declare function useUnmount(callback: () => void, options?: UseUnmountOptions): void;
|
|
97
|
+
|
|
98
|
+
export { type UseUnmountOptions, useUnmount };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Options for useUnmount hook
|
|
3
|
+
*/
|
|
4
|
+
interface UseUnmountOptions {
|
|
5
|
+
/**
|
|
6
|
+
* Whether the unmount callback is enabled.
|
|
7
|
+
* When false, the callback will not be executed on unmount.
|
|
8
|
+
* @default true
|
|
9
|
+
*/
|
|
10
|
+
enabled?: boolean;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Executes a callback function when the component unmounts.
|
|
14
|
+
* The callback always has access to the latest values (closure freshness).
|
|
15
|
+
*
|
|
16
|
+
* Features:
|
|
17
|
+
* - Closure freshness: callback always sees latest state/props
|
|
18
|
+
* - Error handling: errors in callback don't break unmount
|
|
19
|
+
* - Conditional execution via `enabled` option
|
|
20
|
+
* - SSR compatible
|
|
21
|
+
* - TypeScript support
|
|
22
|
+
*
|
|
23
|
+
* @param callback - The function to execute on unmount
|
|
24
|
+
* @param options - Configuration options
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```tsx
|
|
28
|
+
* // Basic usage
|
|
29
|
+
* function MyComponent() {
|
|
30
|
+
* useUnmount(() => {
|
|
31
|
+
* console.log("Component unmounted");
|
|
32
|
+
* });
|
|
33
|
+
*
|
|
34
|
+
* return <div>Hello</div>;
|
|
35
|
+
* }
|
|
36
|
+
* ```
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```tsx
|
|
40
|
+
* // With latest state access
|
|
41
|
+
* function FormComponent() {
|
|
42
|
+
* const [formData, setFormData] = useState({});
|
|
43
|
+
*
|
|
44
|
+
* useUnmount(() => {
|
|
45
|
+
* // formData will have the latest value at unmount time
|
|
46
|
+
* saveToLocalStorage(formData);
|
|
47
|
+
* });
|
|
48
|
+
*
|
|
49
|
+
* return <form>...</form>;
|
|
50
|
+
* }
|
|
51
|
+
* ```
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```tsx
|
|
55
|
+
* // Conditional cleanup
|
|
56
|
+
* function TrackingComponent({ trackingEnabled }) {
|
|
57
|
+
* useUnmount(
|
|
58
|
+
* () => {
|
|
59
|
+
* sendAnalyticsEvent("component_unmounted");
|
|
60
|
+
* },
|
|
61
|
+
* { enabled: trackingEnabled }
|
|
62
|
+
* );
|
|
63
|
+
*
|
|
64
|
+
* return <div>Tracked content</div>;
|
|
65
|
+
* }
|
|
66
|
+
* ```
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* ```tsx
|
|
70
|
+
* // Resource cleanup
|
|
71
|
+
* function WebSocketComponent() {
|
|
72
|
+
* const wsRef = useRef<WebSocket | null>(null);
|
|
73
|
+
*
|
|
74
|
+
* useEffect(() => {
|
|
75
|
+
* wsRef.current = new WebSocket("wss://example.com");
|
|
76
|
+
* }, []);
|
|
77
|
+
*
|
|
78
|
+
* useUnmount(() => {
|
|
79
|
+
* wsRef.current?.close();
|
|
80
|
+
* });
|
|
81
|
+
*
|
|
82
|
+
* return <div>Connected</div>;
|
|
83
|
+
* }
|
|
84
|
+
* ```
|
|
85
|
+
*
|
|
86
|
+
* @remarks
|
|
87
|
+
* **React StrictMode**: In development with StrictMode, React intentionally
|
|
88
|
+
* mounts, unmounts, and remounts components to detect side effects. This means
|
|
89
|
+
* the unmount callback may be called multiple times during development.
|
|
90
|
+
* This is expected behavior and helps identify issues with cleanup logic.
|
|
91
|
+
*
|
|
92
|
+
* **Error Handling**: If the callback throws an error, it will be caught and
|
|
93
|
+
* logged to the console. This prevents unmount errors from breaking
|
|
94
|
+
* the entire component tree unmount process.
|
|
95
|
+
*/
|
|
96
|
+
declare function useUnmount(callback: () => void, options?: UseUnmountOptions): void;
|
|
97
|
+
|
|
98
|
+
export { type UseUnmountOptions, useUnmount };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
useUnmount: () => useUnmount
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(index_exports);
|
|
26
|
+
|
|
27
|
+
// src/useUnmount.ts
|
|
28
|
+
var import_react = require("react");
|
|
29
|
+
function useUnmount(callback, options = {}) {
|
|
30
|
+
const { enabled = true } = options;
|
|
31
|
+
const callbackRef = (0, import_react.useRef)(callback);
|
|
32
|
+
callbackRef.current = callback;
|
|
33
|
+
(0, import_react.useEffect)(() => {
|
|
34
|
+
if (!enabled) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
return () => {
|
|
38
|
+
try {
|
|
39
|
+
callbackRef.current();
|
|
40
|
+
} catch (error) {
|
|
41
|
+
console.error("useUnmount: Error in unmount callback:", error);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
}, [enabled]);
|
|
45
|
+
}
|
|
46
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
47
|
+
0 && (module.exports = {
|
|
48
|
+
useUnmount
|
|
49
|
+
});
|
|
50
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/useUnmount.ts"],"sourcesContent":["export { useUnmount } from \"./useUnmount\";\nexport type { UseUnmountOptions } from \"./useUnmount\";\n","import { useEffect, useRef } from \"react\";\n\n/**\n * Options for useUnmount hook\n */\nexport interface UseUnmountOptions {\n /**\n * Whether the unmount callback is enabled.\n * When false, the callback will not be executed on unmount.\n * @default true\n */\n enabled?: boolean;\n}\n\n/**\n * Executes a callback function when the component unmounts.\n * The callback always has access to the latest values (closure freshness).\n *\n * Features:\n * - Closure freshness: callback always sees latest state/props\n * - Error handling: errors in callback don't break unmount\n * - Conditional execution via `enabled` option\n * - SSR compatible\n * - TypeScript support\n *\n * @param callback - The function to execute on unmount\n * @param options - Configuration options\n *\n * @example\n * ```tsx\n * // Basic usage\n * function MyComponent() {\n * useUnmount(() => {\n * console.log(\"Component unmounted\");\n * });\n *\n * return <div>Hello</div>;\n * }\n * ```\n *\n * @example\n * ```tsx\n * // With latest state access\n * function FormComponent() {\n * const [formData, setFormData] = useState({});\n *\n * useUnmount(() => {\n * // formData will have the latest value at unmount time\n * saveToLocalStorage(formData);\n * });\n *\n * return <form>...</form>;\n * }\n * ```\n *\n * @example\n * ```tsx\n * // Conditional cleanup\n * function TrackingComponent({ trackingEnabled }) {\n * useUnmount(\n * () => {\n * sendAnalyticsEvent(\"component_unmounted\");\n * },\n * { enabled: trackingEnabled }\n * );\n *\n * return <div>Tracked content</div>;\n * }\n * ```\n *\n * @example\n * ```tsx\n * // Resource cleanup\n * function WebSocketComponent() {\n * const wsRef = useRef<WebSocket | null>(null);\n *\n * useEffect(() => {\n * wsRef.current = new WebSocket(\"wss://example.com\");\n * }, []);\n *\n * useUnmount(() => {\n * wsRef.current?.close();\n * });\n *\n * return <div>Connected</div>;\n * }\n * ```\n *\n * @remarks\n * **React StrictMode**: In development with StrictMode, React intentionally\n * mounts, unmounts, and remounts components to detect side effects. This means\n * the unmount callback may be called multiple times during development.\n * This is expected behavior and helps identify issues with cleanup logic.\n *\n * **Error Handling**: If the callback throws an error, it will be caught and\n * logged to the console. This prevents unmount errors from breaking\n * the entire component tree unmount process.\n */\nexport function useUnmount(\n callback: () => void,\n options: UseUnmountOptions = {}\n): void {\n const { enabled = true } = options;\n\n // Store callback in ref to always have the latest version\n // This ensures closure freshness - the callback at unmount time\n // will have access to the most recent state/props values\n const callbackRef = useRef(callback);\n\n // Update the ref on every render to capture latest callback\n callbackRef.current = callback;\n\n useEffect(() => {\n // If disabled, don't set up cleanup\n if (!enabled) {\n return;\n }\n\n // Return cleanup function\n return () => {\n try {\n callbackRef.current();\n } catch (error) {\n // Log the error to help with debugging\n // Catch to prevent breaking other unmounts in the component tree\n console.error(\"useUnmount: Error in unmount callback:\", error);\n }\n };\n }, [enabled]);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,mBAAkC;AAkG3B,SAAS,WACd,UACA,UAA6B,CAAC,GACxB;AACN,QAAM,EAAE,UAAU,KAAK,IAAI;AAK3B,QAAM,kBAAc,qBAAO,QAAQ;AAGnC,cAAY,UAAU;AAEtB,8BAAU,MAAM;AAEd,QAAI,CAAC,SAAS;AACZ;AAAA,IACF;AAGA,WAAO,MAAM;AACX,UAAI;AACF,oBAAY,QAAQ;AAAA,MACtB,SAAS,OAAO;AAGd,gBAAQ,MAAM,0CAA0C,KAAK;AAAA,MAC/D;AAAA,IACF;AAAA,EACF,GAAG,CAAC,OAAO,CAAC;AACd;","names":[]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// src/useUnmount.ts
|
|
2
|
+
import { useEffect, useRef } from "react";
|
|
3
|
+
function useUnmount(callback, options = {}) {
|
|
4
|
+
const { enabled = true } = options;
|
|
5
|
+
const callbackRef = useRef(callback);
|
|
6
|
+
callbackRef.current = callback;
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
if (!enabled) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
return () => {
|
|
12
|
+
try {
|
|
13
|
+
callbackRef.current();
|
|
14
|
+
} catch (error) {
|
|
15
|
+
console.error("useUnmount: Error in unmount callback:", error);
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
}, [enabled]);
|
|
19
|
+
}
|
|
20
|
+
export {
|
|
21
|
+
useUnmount
|
|
22
|
+
};
|
|
23
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/useUnmount.ts"],"sourcesContent":["import { useEffect, useRef } from \"react\";\n\n/**\n * Options for useUnmount hook\n */\nexport interface UseUnmountOptions {\n /**\n * Whether the unmount callback is enabled.\n * When false, the callback will not be executed on unmount.\n * @default true\n */\n enabled?: boolean;\n}\n\n/**\n * Executes a callback function when the component unmounts.\n * The callback always has access to the latest values (closure freshness).\n *\n * Features:\n * - Closure freshness: callback always sees latest state/props\n * - Error handling: errors in callback don't break unmount\n * - Conditional execution via `enabled` option\n * - SSR compatible\n * - TypeScript support\n *\n * @param callback - The function to execute on unmount\n * @param options - Configuration options\n *\n * @example\n * ```tsx\n * // Basic usage\n * function MyComponent() {\n * useUnmount(() => {\n * console.log(\"Component unmounted\");\n * });\n *\n * return <div>Hello</div>;\n * }\n * ```\n *\n * @example\n * ```tsx\n * // With latest state access\n * function FormComponent() {\n * const [formData, setFormData] = useState({});\n *\n * useUnmount(() => {\n * // formData will have the latest value at unmount time\n * saveToLocalStorage(formData);\n * });\n *\n * return <form>...</form>;\n * }\n * ```\n *\n * @example\n * ```tsx\n * // Conditional cleanup\n * function TrackingComponent({ trackingEnabled }) {\n * useUnmount(\n * () => {\n * sendAnalyticsEvent(\"component_unmounted\");\n * },\n * { enabled: trackingEnabled }\n * );\n *\n * return <div>Tracked content</div>;\n * }\n * ```\n *\n * @example\n * ```tsx\n * // Resource cleanup\n * function WebSocketComponent() {\n * const wsRef = useRef<WebSocket | null>(null);\n *\n * useEffect(() => {\n * wsRef.current = new WebSocket(\"wss://example.com\");\n * }, []);\n *\n * useUnmount(() => {\n * wsRef.current?.close();\n * });\n *\n * return <div>Connected</div>;\n * }\n * ```\n *\n * @remarks\n * **React StrictMode**: In development with StrictMode, React intentionally\n * mounts, unmounts, and remounts components to detect side effects. This means\n * the unmount callback may be called multiple times during development.\n * This is expected behavior and helps identify issues with cleanup logic.\n *\n * **Error Handling**: If the callback throws an error, it will be caught and\n * logged to the console. This prevents unmount errors from breaking\n * the entire component tree unmount process.\n */\nexport function useUnmount(\n callback: () => void,\n options: UseUnmountOptions = {}\n): void {\n const { enabled = true } = options;\n\n // Store callback in ref to always have the latest version\n // This ensures closure freshness - the callback at unmount time\n // will have access to the most recent state/props values\n const callbackRef = useRef(callback);\n\n // Update the ref on every render to capture latest callback\n callbackRef.current = callback;\n\n useEffect(() => {\n // If disabled, don't set up cleanup\n if (!enabled) {\n return;\n }\n\n // Return cleanup function\n return () => {\n try {\n callbackRef.current();\n } catch (error) {\n // Log the error to help with debugging\n // Catch to prevent breaking other unmounts in the component tree\n console.error(\"useUnmount: Error in unmount callback:\", error);\n }\n };\n }, [enabled]);\n}\n"],"mappings":";AAAA,SAAS,WAAW,cAAc;AAkG3B,SAAS,WACd,UACA,UAA6B,CAAC,GACxB;AACN,QAAM,EAAE,UAAU,KAAK,IAAI;AAK3B,QAAM,cAAc,OAAO,QAAQ;AAGnC,cAAY,UAAU;AAEtB,YAAU,MAAM;AAEd,QAAI,CAAC,SAAS;AACZ;AAAA,IACF;AAGA,WAAO,MAAM;AACX,UAAI;AACF,oBAAY,QAAQ;AAAA,MACtB,SAAS,OAAO;AAGd,gBAAQ,MAAM,0CAA0C,KAAK;AAAA,MAC/D;AAAA,IACF;AAAA,EACF,GAAG,CAAC,OAAO,CAAC;AACd;","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@usefy/use-unmount",
|
|
3
|
+
"version": "0.0.20",
|
|
4
|
+
"description": "A React hook that executes a callback when the component unmounts",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.mjs",
|
|
12
|
+
"require": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"sideEffects": false,
|
|
19
|
+
"peerDependencies": {
|
|
20
|
+
"react": "^18.0.0 || ^19.0.0"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@testing-library/jest-dom": "^6.9.1",
|
|
24
|
+
"@testing-library/react": "^16.3.1",
|
|
25
|
+
"@types/react": "^19.0.0",
|
|
26
|
+
"jsdom": "^27.3.0",
|
|
27
|
+
"react": "^19.0.0",
|
|
28
|
+
"rimraf": "^6.0.1",
|
|
29
|
+
"tsup": "^8.0.0",
|
|
30
|
+
"typescript": "^5.0.0",
|
|
31
|
+
"vitest": "^4.0.16"
|
|
32
|
+
},
|
|
33
|
+
"publishConfig": {
|
|
34
|
+
"access": "public"
|
|
35
|
+
},
|
|
36
|
+
"repository": {
|
|
37
|
+
"type": "git",
|
|
38
|
+
"url": "https://github.com/geon0529/usefy.git",
|
|
39
|
+
"directory": "packages/use-unmount"
|
|
40
|
+
},
|
|
41
|
+
"license": "MIT",
|
|
42
|
+
"keywords": [
|
|
43
|
+
"react",
|
|
44
|
+
"hooks",
|
|
45
|
+
"unmount",
|
|
46
|
+
"cleanup",
|
|
47
|
+
"lifecycle",
|
|
48
|
+
"useUnmount",
|
|
49
|
+
"useEffect"
|
|
50
|
+
],
|
|
51
|
+
"scripts": {
|
|
52
|
+
"build": "tsup",
|
|
53
|
+
"dev": "tsup --watch",
|
|
54
|
+
"test": "vitest run",
|
|
55
|
+
"test:watch": "vitest",
|
|
56
|
+
"typecheck": "tsc --noEmit",
|
|
57
|
+
"clean": "rimraf dist"
|
|
58
|
+
}
|
|
59
|
+
}
|