@usefy/use-session-storage 0.0.2
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/dist/index.d.mts +93 -0
- package/dist/index.d.ts +93 -0
- package/dist/index.js +114 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +87 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +59 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type for initial value that can be a value or a function returning a value (lazy initialization)
|
|
3
|
+
*/
|
|
4
|
+
type InitialValue<T> = T | (() => T);
|
|
5
|
+
/**
|
|
6
|
+
* Options for useSessionStorage hook
|
|
7
|
+
*/
|
|
8
|
+
interface UseSessionStorageOptions<T> {
|
|
9
|
+
/**
|
|
10
|
+
* Custom serializer function for converting value to string
|
|
11
|
+
* @default JSON.stringify
|
|
12
|
+
*/
|
|
13
|
+
serializer?: (value: T) => string;
|
|
14
|
+
/**
|
|
15
|
+
* Custom deserializer function for parsing stored string to value
|
|
16
|
+
* @default JSON.parse
|
|
17
|
+
*/
|
|
18
|
+
deserializer?: (value: string) => T;
|
|
19
|
+
/**
|
|
20
|
+
* Callback function called when an error occurs
|
|
21
|
+
*/
|
|
22
|
+
onError?: (error: Error) => void;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Return type for useSessionStorage hook - tuple similar to useState
|
|
26
|
+
*/
|
|
27
|
+
type UseSessionStorageReturn<T> = readonly [
|
|
28
|
+
/** Current stored value */
|
|
29
|
+
T,
|
|
30
|
+
/** Function to update the value (same signature as useState setter) */
|
|
31
|
+
React.Dispatch<React.SetStateAction<T>>,
|
|
32
|
+
/** Function to remove the value from sessionStorage */
|
|
33
|
+
() => void
|
|
34
|
+
];
|
|
35
|
+
/**
|
|
36
|
+
* A hook for persisting state in sessionStorage.
|
|
37
|
+
* Works like useState but persists the value in sessionStorage for the duration of the browser session.
|
|
38
|
+
*
|
|
39
|
+
* Unlike localStorage, sessionStorage data:
|
|
40
|
+
* - Is cleared when the tab/window is closed
|
|
41
|
+
* - Is not shared between tabs (each tab has its own session)
|
|
42
|
+
*
|
|
43
|
+
* @template T - The type of the stored value
|
|
44
|
+
* @param key - The sessionStorage key to store the value under
|
|
45
|
+
* @param initialValue - Initial value or function returning initial value (lazy initialization)
|
|
46
|
+
* @param options - Configuration options for serialization and error handling
|
|
47
|
+
* @returns A tuple of [storedValue, setValue, removeValue]
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```tsx
|
|
51
|
+
* // Basic usage - form data that persists during session
|
|
52
|
+
* function CheckoutForm() {
|
|
53
|
+
* const [formData, setFormData, clearForm] = useSessionStorage('checkout-form', {
|
|
54
|
+
* name: '',
|
|
55
|
+
* email: '',
|
|
56
|
+
* });
|
|
57
|
+
*
|
|
58
|
+
* return (
|
|
59
|
+
* <form>
|
|
60
|
+
* <input
|
|
61
|
+
* value={formData.name}
|
|
62
|
+
* onChange={(e) => setFormData(prev => ({ ...prev, name: e.target.value }))}
|
|
63
|
+
* />
|
|
64
|
+
* <button type="button" onClick={clearForm}>Clear</button>
|
|
65
|
+
* </form>
|
|
66
|
+
* );
|
|
67
|
+
* }
|
|
68
|
+
* ```
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```tsx
|
|
72
|
+
* // Temporary state that resets on tab close
|
|
73
|
+
* const [wizardStep, setWizardStep] = useSessionStorage('wizard-step', 1);
|
|
74
|
+
* ```
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* ```tsx
|
|
78
|
+
* // With lazy initialization
|
|
79
|
+
* const [cache, setCache] = useSessionStorage('cache', () => computeInitialCache());
|
|
80
|
+
* ```
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* ```tsx
|
|
84
|
+
* // With custom serializer/deserializer
|
|
85
|
+
* const [date, setDate] = useSessionStorage<Date>('lastAction', new Date(), {
|
|
86
|
+
* serializer: (d) => d.toISOString(),
|
|
87
|
+
* deserializer: (s) => new Date(s),
|
|
88
|
+
* });
|
|
89
|
+
* ```
|
|
90
|
+
*/
|
|
91
|
+
declare function useSessionStorage<T>(key: string, initialValue: InitialValue<T>, options?: UseSessionStorageOptions<T>): UseSessionStorageReturn<T>;
|
|
92
|
+
|
|
93
|
+
export { type InitialValue, type UseSessionStorageOptions, type UseSessionStorageReturn, useSessionStorage };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type for initial value that can be a value or a function returning a value (lazy initialization)
|
|
3
|
+
*/
|
|
4
|
+
type InitialValue<T> = T | (() => T);
|
|
5
|
+
/**
|
|
6
|
+
* Options for useSessionStorage hook
|
|
7
|
+
*/
|
|
8
|
+
interface UseSessionStorageOptions<T> {
|
|
9
|
+
/**
|
|
10
|
+
* Custom serializer function for converting value to string
|
|
11
|
+
* @default JSON.stringify
|
|
12
|
+
*/
|
|
13
|
+
serializer?: (value: T) => string;
|
|
14
|
+
/**
|
|
15
|
+
* Custom deserializer function for parsing stored string to value
|
|
16
|
+
* @default JSON.parse
|
|
17
|
+
*/
|
|
18
|
+
deserializer?: (value: string) => T;
|
|
19
|
+
/**
|
|
20
|
+
* Callback function called when an error occurs
|
|
21
|
+
*/
|
|
22
|
+
onError?: (error: Error) => void;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Return type for useSessionStorage hook - tuple similar to useState
|
|
26
|
+
*/
|
|
27
|
+
type UseSessionStorageReturn<T> = readonly [
|
|
28
|
+
/** Current stored value */
|
|
29
|
+
T,
|
|
30
|
+
/** Function to update the value (same signature as useState setter) */
|
|
31
|
+
React.Dispatch<React.SetStateAction<T>>,
|
|
32
|
+
/** Function to remove the value from sessionStorage */
|
|
33
|
+
() => void
|
|
34
|
+
];
|
|
35
|
+
/**
|
|
36
|
+
* A hook for persisting state in sessionStorage.
|
|
37
|
+
* Works like useState but persists the value in sessionStorage for the duration of the browser session.
|
|
38
|
+
*
|
|
39
|
+
* Unlike localStorage, sessionStorage data:
|
|
40
|
+
* - Is cleared when the tab/window is closed
|
|
41
|
+
* - Is not shared between tabs (each tab has its own session)
|
|
42
|
+
*
|
|
43
|
+
* @template T - The type of the stored value
|
|
44
|
+
* @param key - The sessionStorage key to store the value under
|
|
45
|
+
* @param initialValue - Initial value or function returning initial value (lazy initialization)
|
|
46
|
+
* @param options - Configuration options for serialization and error handling
|
|
47
|
+
* @returns A tuple of [storedValue, setValue, removeValue]
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```tsx
|
|
51
|
+
* // Basic usage - form data that persists during session
|
|
52
|
+
* function CheckoutForm() {
|
|
53
|
+
* const [formData, setFormData, clearForm] = useSessionStorage('checkout-form', {
|
|
54
|
+
* name: '',
|
|
55
|
+
* email: '',
|
|
56
|
+
* });
|
|
57
|
+
*
|
|
58
|
+
* return (
|
|
59
|
+
* <form>
|
|
60
|
+
* <input
|
|
61
|
+
* value={formData.name}
|
|
62
|
+
* onChange={(e) => setFormData(prev => ({ ...prev, name: e.target.value }))}
|
|
63
|
+
* />
|
|
64
|
+
* <button type="button" onClick={clearForm}>Clear</button>
|
|
65
|
+
* </form>
|
|
66
|
+
* );
|
|
67
|
+
* }
|
|
68
|
+
* ```
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```tsx
|
|
72
|
+
* // Temporary state that resets on tab close
|
|
73
|
+
* const [wizardStep, setWizardStep] = useSessionStorage('wizard-step', 1);
|
|
74
|
+
* ```
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* ```tsx
|
|
78
|
+
* // With lazy initialization
|
|
79
|
+
* const [cache, setCache] = useSessionStorage('cache', () => computeInitialCache());
|
|
80
|
+
* ```
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* ```tsx
|
|
84
|
+
* // With custom serializer/deserializer
|
|
85
|
+
* const [date, setDate] = useSessionStorage<Date>('lastAction', new Date(), {
|
|
86
|
+
* serializer: (d) => d.toISOString(),
|
|
87
|
+
* deserializer: (s) => new Date(s),
|
|
88
|
+
* });
|
|
89
|
+
* ```
|
|
90
|
+
*/
|
|
91
|
+
declare function useSessionStorage<T>(key: string, initialValue: InitialValue<T>, options?: UseSessionStorageOptions<T>): UseSessionStorageReturn<T>;
|
|
92
|
+
|
|
93
|
+
export { type InitialValue, type UseSessionStorageOptions, type UseSessionStorageReturn, useSessionStorage };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
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
|
+
useSessionStorage: () => useSessionStorage
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(index_exports);
|
|
26
|
+
|
|
27
|
+
// src/useSessionStorage.ts
|
|
28
|
+
var import_react = require("react");
|
|
29
|
+
function resolveInitialValue(initialValue) {
|
|
30
|
+
return typeof initialValue === "function" ? initialValue() : initialValue;
|
|
31
|
+
}
|
|
32
|
+
function useSessionStorage(key, initialValue, options = {}) {
|
|
33
|
+
const {
|
|
34
|
+
serializer = JSON.stringify,
|
|
35
|
+
deserializer = JSON.parse,
|
|
36
|
+
onError
|
|
37
|
+
} = options;
|
|
38
|
+
const serializerRef = (0, import_react.useRef)(serializer);
|
|
39
|
+
const deserializerRef = (0, import_react.useRef)(deserializer);
|
|
40
|
+
const onErrorRef = (0, import_react.useRef)(onError);
|
|
41
|
+
serializerRef.current = serializer;
|
|
42
|
+
deserializerRef.current = deserializer;
|
|
43
|
+
onErrorRef.current = onError;
|
|
44
|
+
const initialValueRef = (0, import_react.useRef)(initialValue);
|
|
45
|
+
initialValueRef.current = initialValue;
|
|
46
|
+
const isClient = typeof window !== "undefined";
|
|
47
|
+
const [storedValue, setStoredValue] = (0, import_react.useState)(() => {
|
|
48
|
+
if (!isClient) {
|
|
49
|
+
return resolveInitialValue(initialValue);
|
|
50
|
+
}
|
|
51
|
+
try {
|
|
52
|
+
const item = window.sessionStorage.getItem(key);
|
|
53
|
+
if (item !== null) {
|
|
54
|
+
return deserializerRef.current(item);
|
|
55
|
+
}
|
|
56
|
+
return resolveInitialValue(initialValue);
|
|
57
|
+
} catch (error) {
|
|
58
|
+
onErrorRef.current?.(error);
|
|
59
|
+
return resolveInitialValue(initialValue);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
const storedValueRef = (0, import_react.useRef)(storedValue);
|
|
63
|
+
storedValueRef.current = storedValue;
|
|
64
|
+
const setValue = (0, import_react.useCallback)(
|
|
65
|
+
(value) => {
|
|
66
|
+
try {
|
|
67
|
+
const currentValue = storedValueRef.current;
|
|
68
|
+
const valueToStore = value instanceof Function ? value(currentValue) : value;
|
|
69
|
+
setStoredValue(valueToStore);
|
|
70
|
+
if (typeof window !== "undefined") {
|
|
71
|
+
window.sessionStorage.setItem(
|
|
72
|
+
key,
|
|
73
|
+
serializerRef.current(valueToStore)
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
} catch (error) {
|
|
77
|
+
onErrorRef.current?.(error);
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
[key]
|
|
81
|
+
);
|
|
82
|
+
const removeValue = (0, import_react.useCallback)(() => {
|
|
83
|
+
try {
|
|
84
|
+
const initial = resolveInitialValue(initialValueRef.current);
|
|
85
|
+
setStoredValue(initial);
|
|
86
|
+
if (typeof window !== "undefined") {
|
|
87
|
+
window.sessionStorage.removeItem(key);
|
|
88
|
+
}
|
|
89
|
+
} catch (error) {
|
|
90
|
+
onErrorRef.current?.(error);
|
|
91
|
+
}
|
|
92
|
+
}, [key]);
|
|
93
|
+
(0, import_react.useEffect)(() => {
|
|
94
|
+
if (!isClient) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
try {
|
|
98
|
+
const item = window.sessionStorage.getItem(key);
|
|
99
|
+
if (item !== null) {
|
|
100
|
+
setStoredValue(deserializerRef.current(item));
|
|
101
|
+
} else {
|
|
102
|
+
setStoredValue(resolveInitialValue(initialValueRef.current));
|
|
103
|
+
}
|
|
104
|
+
} catch {
|
|
105
|
+
setStoredValue(resolveInitialValue(initialValueRef.current));
|
|
106
|
+
}
|
|
107
|
+
}, [key]);
|
|
108
|
+
return [storedValue, setValue, removeValue];
|
|
109
|
+
}
|
|
110
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
111
|
+
0 && (module.exports = {
|
|
112
|
+
useSessionStorage
|
|
113
|
+
});
|
|
114
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/useSessionStorage.ts"],"sourcesContent":["export {\r\n useSessionStorage,\r\n type UseSessionStorageOptions,\r\n type UseSessionStorageReturn,\r\n type InitialValue,\r\n} from \"./useSessionStorage\";\r\n","import { useCallback, useEffect, useRef, useState } from \"react\";\r\n\r\n/**\r\n * Type for initial value that can be a value or a function returning a value (lazy initialization)\r\n */\r\nexport type InitialValue<T> = T | (() => T);\r\n\r\n/**\r\n * Options for useSessionStorage hook\r\n */\r\nexport interface UseSessionStorageOptions<T> {\r\n /**\r\n * Custom serializer function for converting value to string\r\n * @default JSON.stringify\r\n */\r\n serializer?: (value: T) => string;\r\n /**\r\n * Custom deserializer function for parsing stored string to value\r\n * @default JSON.parse\r\n */\r\n deserializer?: (value: string) => T;\r\n /**\r\n * Callback function called when an error occurs\r\n */\r\n onError?: (error: Error) => void;\r\n}\r\n\r\n/**\r\n * Return type for useSessionStorage hook - tuple similar to useState\r\n */\r\nexport type UseSessionStorageReturn<T> = readonly [\r\n /** Current stored value */\r\n T,\r\n /** Function to update the value (same signature as useState setter) */\r\n React.Dispatch<React.SetStateAction<T>>,\r\n /** Function to remove the value from sessionStorage */\r\n () => void\r\n];\r\n\r\n/**\r\n * Helper function to resolve initial value (supports lazy initialization)\r\n */\r\nfunction resolveInitialValue<T>(initialValue: InitialValue<T>): T {\r\n return typeof initialValue === \"function\"\r\n ? (initialValue as () => T)()\r\n : initialValue;\r\n}\r\n\r\n/**\r\n * A hook for persisting state in sessionStorage.\r\n * Works like useState but persists the value in sessionStorage for the duration of the browser session.\r\n *\r\n * Unlike localStorage, sessionStorage data:\r\n * - Is cleared when the tab/window is closed\r\n * - Is not shared between tabs (each tab has its own session)\r\n *\r\n * @template T - The type of the stored value\r\n * @param key - The sessionStorage key to store the value under\r\n * @param initialValue - Initial value or function returning initial value (lazy initialization)\r\n * @param options - Configuration options for serialization and error handling\r\n * @returns A tuple of [storedValue, setValue, removeValue]\r\n *\r\n * @example\r\n * ```tsx\r\n * // Basic usage - form data that persists during session\r\n * function CheckoutForm() {\r\n * const [formData, setFormData, clearForm] = useSessionStorage('checkout-form', {\r\n * name: '',\r\n * email: '',\r\n * });\r\n *\r\n * return (\r\n * <form>\r\n * <input\r\n * value={formData.name}\r\n * onChange={(e) => setFormData(prev => ({ ...prev, name: e.target.value }))}\r\n * />\r\n * <button type=\"button\" onClick={clearForm}>Clear</button>\r\n * </form>\r\n * );\r\n * }\r\n * ```\r\n *\r\n * @example\r\n * ```tsx\r\n * // Temporary state that resets on tab close\r\n * const [wizardStep, setWizardStep] = useSessionStorage('wizard-step', 1);\r\n * ```\r\n *\r\n * @example\r\n * ```tsx\r\n * // With lazy initialization\r\n * const [cache, setCache] = useSessionStorage('cache', () => computeInitialCache());\r\n * ```\r\n *\r\n * @example\r\n * ```tsx\r\n * // With custom serializer/deserializer\r\n * const [date, setDate] = useSessionStorage<Date>('lastAction', new Date(), {\r\n * serializer: (d) => d.toISOString(),\r\n * deserializer: (s) => new Date(s),\r\n * });\r\n * ```\r\n */\r\nexport function useSessionStorage<T>(\r\n key: string,\r\n initialValue: InitialValue<T>,\r\n options: UseSessionStorageOptions<T> = {}\r\n): UseSessionStorageReturn<T> {\r\n const {\r\n serializer = JSON.stringify,\r\n deserializer = JSON.parse,\r\n onError,\r\n } = options;\r\n\r\n // Store options in refs for stable references and access to latest values\r\n const serializerRef = useRef(serializer);\r\n const deserializerRef = useRef(deserializer);\r\n const onErrorRef = useRef(onError);\r\n serializerRef.current = serializer;\r\n deserializerRef.current = deserializer;\r\n onErrorRef.current = onError;\r\n\r\n // Store initialValue in ref for use in removeValue\r\n const initialValueRef = useRef(initialValue);\r\n initialValueRef.current = initialValue;\r\n\r\n // SSR check\r\n const isClient = typeof window !== \"undefined\";\r\n\r\n // Lazy initialization with sessionStorage read\r\n const [storedValue, setStoredValue] = useState<T>(() => {\r\n if (!isClient) {\r\n return resolveInitialValue(initialValue);\r\n }\r\n\r\n try {\r\n const item = window.sessionStorage.getItem(key);\r\n if (item !== null) {\r\n return deserializerRef.current(item);\r\n }\r\n return resolveInitialValue(initialValue);\r\n } catch (error) {\r\n onErrorRef.current?.(error as Error);\r\n return resolveInitialValue(initialValue);\r\n }\r\n });\r\n\r\n // Store current value in ref for stable setValue reference\r\n const storedValueRef = useRef<T>(storedValue);\r\n storedValueRef.current = storedValue;\r\n\r\n // setValue - stable reference (only depends on key)\r\n const setValue = useCallback<React.Dispatch<React.SetStateAction<T>>>(\r\n (value) => {\r\n try {\r\n const currentValue = storedValueRef.current;\r\n const valueToStore =\r\n value instanceof Function ? value(currentValue) : value;\r\n\r\n setStoredValue(valueToStore);\r\n\r\n if (typeof window !== \"undefined\") {\r\n window.sessionStorage.setItem(\r\n key,\r\n serializerRef.current(valueToStore)\r\n );\r\n }\r\n } catch (error) {\r\n onErrorRef.current?.(error as Error);\r\n }\r\n },\r\n [key]\r\n );\r\n\r\n // removeValue - stable reference\r\n const removeValue = useCallback(() => {\r\n try {\r\n const initial = resolveInitialValue(initialValueRef.current);\r\n setStoredValue(initial);\r\n\r\n if (typeof window !== \"undefined\") {\r\n window.sessionStorage.removeItem(key);\r\n }\r\n } catch (error) {\r\n onErrorRef.current?.(error as Error);\r\n }\r\n }, [key]);\r\n\r\n // Re-read value when key changes\r\n useEffect(() => {\r\n if (!isClient) {\r\n return;\r\n }\r\n\r\n try {\r\n const item = window.sessionStorage.getItem(key);\r\n if (item !== null) {\r\n setStoredValue(deserializerRef.current(item));\r\n } else {\r\n setStoredValue(resolveInitialValue(initialValueRef.current));\r\n }\r\n } catch {\r\n setStoredValue(resolveInitialValue(initialValueRef.current));\r\n }\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n }, [key]);\r\n\r\n return [storedValue, setValue, removeValue] as const;\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,mBAAyD;AA0CzD,SAAS,oBAAuB,cAAkC;AAChE,SAAO,OAAO,iBAAiB,aAC1B,aAAyB,IAC1B;AACN;AA0DO,SAAS,kBACd,KACA,cACA,UAAuC,CAAC,GACZ;AAC5B,QAAM;AAAA,IACJ,aAAa,KAAK;AAAA,IAClB,eAAe,KAAK;AAAA,IACpB;AAAA,EACF,IAAI;AAGJ,QAAM,oBAAgB,qBAAO,UAAU;AACvC,QAAM,sBAAkB,qBAAO,YAAY;AAC3C,QAAM,iBAAa,qBAAO,OAAO;AACjC,gBAAc,UAAU;AACxB,kBAAgB,UAAU;AAC1B,aAAW,UAAU;AAGrB,QAAM,sBAAkB,qBAAO,YAAY;AAC3C,kBAAgB,UAAU;AAG1B,QAAM,WAAW,OAAO,WAAW;AAGnC,QAAM,CAAC,aAAa,cAAc,QAAI,uBAAY,MAAM;AACtD,QAAI,CAAC,UAAU;AACb,aAAO,oBAAoB,YAAY;AAAA,IACzC;AAEA,QAAI;AACF,YAAM,OAAO,OAAO,eAAe,QAAQ,GAAG;AAC9C,UAAI,SAAS,MAAM;AACjB,eAAO,gBAAgB,QAAQ,IAAI;AAAA,MACrC;AACA,aAAO,oBAAoB,YAAY;AAAA,IACzC,SAAS,OAAO;AACd,iBAAW,UAAU,KAAc;AACnC,aAAO,oBAAoB,YAAY;AAAA,IACzC;AAAA,EACF,CAAC;AAGD,QAAM,qBAAiB,qBAAU,WAAW;AAC5C,iBAAe,UAAU;AAGzB,QAAM,eAAW;AAAA,IACf,CAAC,UAAU;AACT,UAAI;AACF,cAAM,eAAe,eAAe;AACpC,cAAM,eACJ,iBAAiB,WAAW,MAAM,YAAY,IAAI;AAEpD,uBAAe,YAAY;AAE3B,YAAI,OAAO,WAAW,aAAa;AACjC,iBAAO,eAAe;AAAA,YACpB;AAAA,YACA,cAAc,QAAQ,YAAY;AAAA,UACpC;AAAA,QACF;AAAA,MACF,SAAS,OAAO;AACd,mBAAW,UAAU,KAAc;AAAA,MACrC;AAAA,IACF;AAAA,IACA,CAAC,GAAG;AAAA,EACN;AAGA,QAAM,kBAAc,0BAAY,MAAM;AACpC,QAAI;AACF,YAAM,UAAU,oBAAoB,gBAAgB,OAAO;AAC3D,qBAAe,OAAO;AAEtB,UAAI,OAAO,WAAW,aAAa;AACjC,eAAO,eAAe,WAAW,GAAG;AAAA,MACtC;AAAA,IACF,SAAS,OAAO;AACd,iBAAW,UAAU,KAAc;AAAA,IACrC;AAAA,EACF,GAAG,CAAC,GAAG,CAAC;AAGR,8BAAU,MAAM;AACd,QAAI,CAAC,UAAU;AACb;AAAA,IACF;AAEA,QAAI;AACF,YAAM,OAAO,OAAO,eAAe,QAAQ,GAAG;AAC9C,UAAI,SAAS,MAAM;AACjB,uBAAe,gBAAgB,QAAQ,IAAI,CAAC;AAAA,MAC9C,OAAO;AACL,uBAAe,oBAAoB,gBAAgB,OAAO,CAAC;AAAA,MAC7D;AAAA,IACF,QAAQ;AACN,qBAAe,oBAAoB,gBAAgB,OAAO,CAAC;AAAA,IAC7D;AAAA,EAEF,GAAG,CAAC,GAAG,CAAC;AAER,SAAO,CAAC,aAAa,UAAU,WAAW;AAC5C;","names":[]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// src/useSessionStorage.ts
|
|
2
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
3
|
+
function resolveInitialValue(initialValue) {
|
|
4
|
+
return typeof initialValue === "function" ? initialValue() : initialValue;
|
|
5
|
+
}
|
|
6
|
+
function useSessionStorage(key, initialValue, options = {}) {
|
|
7
|
+
const {
|
|
8
|
+
serializer = JSON.stringify,
|
|
9
|
+
deserializer = JSON.parse,
|
|
10
|
+
onError
|
|
11
|
+
} = options;
|
|
12
|
+
const serializerRef = useRef(serializer);
|
|
13
|
+
const deserializerRef = useRef(deserializer);
|
|
14
|
+
const onErrorRef = useRef(onError);
|
|
15
|
+
serializerRef.current = serializer;
|
|
16
|
+
deserializerRef.current = deserializer;
|
|
17
|
+
onErrorRef.current = onError;
|
|
18
|
+
const initialValueRef = useRef(initialValue);
|
|
19
|
+
initialValueRef.current = initialValue;
|
|
20
|
+
const isClient = typeof window !== "undefined";
|
|
21
|
+
const [storedValue, setStoredValue] = useState(() => {
|
|
22
|
+
if (!isClient) {
|
|
23
|
+
return resolveInitialValue(initialValue);
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
const item = window.sessionStorage.getItem(key);
|
|
27
|
+
if (item !== null) {
|
|
28
|
+
return deserializerRef.current(item);
|
|
29
|
+
}
|
|
30
|
+
return resolveInitialValue(initialValue);
|
|
31
|
+
} catch (error) {
|
|
32
|
+
onErrorRef.current?.(error);
|
|
33
|
+
return resolveInitialValue(initialValue);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
const storedValueRef = useRef(storedValue);
|
|
37
|
+
storedValueRef.current = storedValue;
|
|
38
|
+
const setValue = useCallback(
|
|
39
|
+
(value) => {
|
|
40
|
+
try {
|
|
41
|
+
const currentValue = storedValueRef.current;
|
|
42
|
+
const valueToStore = value instanceof Function ? value(currentValue) : value;
|
|
43
|
+
setStoredValue(valueToStore);
|
|
44
|
+
if (typeof window !== "undefined") {
|
|
45
|
+
window.sessionStorage.setItem(
|
|
46
|
+
key,
|
|
47
|
+
serializerRef.current(valueToStore)
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
} catch (error) {
|
|
51
|
+
onErrorRef.current?.(error);
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
[key]
|
|
55
|
+
);
|
|
56
|
+
const removeValue = useCallback(() => {
|
|
57
|
+
try {
|
|
58
|
+
const initial = resolveInitialValue(initialValueRef.current);
|
|
59
|
+
setStoredValue(initial);
|
|
60
|
+
if (typeof window !== "undefined") {
|
|
61
|
+
window.sessionStorage.removeItem(key);
|
|
62
|
+
}
|
|
63
|
+
} catch (error) {
|
|
64
|
+
onErrorRef.current?.(error);
|
|
65
|
+
}
|
|
66
|
+
}, [key]);
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
if (!isClient) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
const item = window.sessionStorage.getItem(key);
|
|
73
|
+
if (item !== null) {
|
|
74
|
+
setStoredValue(deserializerRef.current(item));
|
|
75
|
+
} else {
|
|
76
|
+
setStoredValue(resolveInitialValue(initialValueRef.current));
|
|
77
|
+
}
|
|
78
|
+
} catch {
|
|
79
|
+
setStoredValue(resolveInitialValue(initialValueRef.current));
|
|
80
|
+
}
|
|
81
|
+
}, [key]);
|
|
82
|
+
return [storedValue, setValue, removeValue];
|
|
83
|
+
}
|
|
84
|
+
export {
|
|
85
|
+
useSessionStorage
|
|
86
|
+
};
|
|
87
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/useSessionStorage.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from \"react\";\r\n\r\n/**\r\n * Type for initial value that can be a value or a function returning a value (lazy initialization)\r\n */\r\nexport type InitialValue<T> = T | (() => T);\r\n\r\n/**\r\n * Options for useSessionStorage hook\r\n */\r\nexport interface UseSessionStorageOptions<T> {\r\n /**\r\n * Custom serializer function for converting value to string\r\n * @default JSON.stringify\r\n */\r\n serializer?: (value: T) => string;\r\n /**\r\n * Custom deserializer function for parsing stored string to value\r\n * @default JSON.parse\r\n */\r\n deserializer?: (value: string) => T;\r\n /**\r\n * Callback function called when an error occurs\r\n */\r\n onError?: (error: Error) => void;\r\n}\r\n\r\n/**\r\n * Return type for useSessionStorage hook - tuple similar to useState\r\n */\r\nexport type UseSessionStorageReturn<T> = readonly [\r\n /** Current stored value */\r\n T,\r\n /** Function to update the value (same signature as useState setter) */\r\n React.Dispatch<React.SetStateAction<T>>,\r\n /** Function to remove the value from sessionStorage */\r\n () => void\r\n];\r\n\r\n/**\r\n * Helper function to resolve initial value (supports lazy initialization)\r\n */\r\nfunction resolveInitialValue<T>(initialValue: InitialValue<T>): T {\r\n return typeof initialValue === \"function\"\r\n ? (initialValue as () => T)()\r\n : initialValue;\r\n}\r\n\r\n/**\r\n * A hook for persisting state in sessionStorage.\r\n * Works like useState but persists the value in sessionStorage for the duration of the browser session.\r\n *\r\n * Unlike localStorage, sessionStorage data:\r\n * - Is cleared when the tab/window is closed\r\n * - Is not shared between tabs (each tab has its own session)\r\n *\r\n * @template T - The type of the stored value\r\n * @param key - The sessionStorage key to store the value under\r\n * @param initialValue - Initial value or function returning initial value (lazy initialization)\r\n * @param options - Configuration options for serialization and error handling\r\n * @returns A tuple of [storedValue, setValue, removeValue]\r\n *\r\n * @example\r\n * ```tsx\r\n * // Basic usage - form data that persists during session\r\n * function CheckoutForm() {\r\n * const [formData, setFormData, clearForm] = useSessionStorage('checkout-form', {\r\n * name: '',\r\n * email: '',\r\n * });\r\n *\r\n * return (\r\n * <form>\r\n * <input\r\n * value={formData.name}\r\n * onChange={(e) => setFormData(prev => ({ ...prev, name: e.target.value }))}\r\n * />\r\n * <button type=\"button\" onClick={clearForm}>Clear</button>\r\n * </form>\r\n * );\r\n * }\r\n * ```\r\n *\r\n * @example\r\n * ```tsx\r\n * // Temporary state that resets on tab close\r\n * const [wizardStep, setWizardStep] = useSessionStorage('wizard-step', 1);\r\n * ```\r\n *\r\n * @example\r\n * ```tsx\r\n * // With lazy initialization\r\n * const [cache, setCache] = useSessionStorage('cache', () => computeInitialCache());\r\n * ```\r\n *\r\n * @example\r\n * ```tsx\r\n * // With custom serializer/deserializer\r\n * const [date, setDate] = useSessionStorage<Date>('lastAction', new Date(), {\r\n * serializer: (d) => d.toISOString(),\r\n * deserializer: (s) => new Date(s),\r\n * });\r\n * ```\r\n */\r\nexport function useSessionStorage<T>(\r\n key: string,\r\n initialValue: InitialValue<T>,\r\n options: UseSessionStorageOptions<T> = {}\r\n): UseSessionStorageReturn<T> {\r\n const {\r\n serializer = JSON.stringify,\r\n deserializer = JSON.parse,\r\n onError,\r\n } = options;\r\n\r\n // Store options in refs for stable references and access to latest values\r\n const serializerRef = useRef(serializer);\r\n const deserializerRef = useRef(deserializer);\r\n const onErrorRef = useRef(onError);\r\n serializerRef.current = serializer;\r\n deserializerRef.current = deserializer;\r\n onErrorRef.current = onError;\r\n\r\n // Store initialValue in ref for use in removeValue\r\n const initialValueRef = useRef(initialValue);\r\n initialValueRef.current = initialValue;\r\n\r\n // SSR check\r\n const isClient = typeof window !== \"undefined\";\r\n\r\n // Lazy initialization with sessionStorage read\r\n const [storedValue, setStoredValue] = useState<T>(() => {\r\n if (!isClient) {\r\n return resolveInitialValue(initialValue);\r\n }\r\n\r\n try {\r\n const item = window.sessionStorage.getItem(key);\r\n if (item !== null) {\r\n return deserializerRef.current(item);\r\n }\r\n return resolveInitialValue(initialValue);\r\n } catch (error) {\r\n onErrorRef.current?.(error as Error);\r\n return resolveInitialValue(initialValue);\r\n }\r\n });\r\n\r\n // Store current value in ref for stable setValue reference\r\n const storedValueRef = useRef<T>(storedValue);\r\n storedValueRef.current = storedValue;\r\n\r\n // setValue - stable reference (only depends on key)\r\n const setValue = useCallback<React.Dispatch<React.SetStateAction<T>>>(\r\n (value) => {\r\n try {\r\n const currentValue = storedValueRef.current;\r\n const valueToStore =\r\n value instanceof Function ? value(currentValue) : value;\r\n\r\n setStoredValue(valueToStore);\r\n\r\n if (typeof window !== \"undefined\") {\r\n window.sessionStorage.setItem(\r\n key,\r\n serializerRef.current(valueToStore)\r\n );\r\n }\r\n } catch (error) {\r\n onErrorRef.current?.(error as Error);\r\n }\r\n },\r\n [key]\r\n );\r\n\r\n // removeValue - stable reference\r\n const removeValue = useCallback(() => {\r\n try {\r\n const initial = resolveInitialValue(initialValueRef.current);\r\n setStoredValue(initial);\r\n\r\n if (typeof window !== \"undefined\") {\r\n window.sessionStorage.removeItem(key);\r\n }\r\n } catch (error) {\r\n onErrorRef.current?.(error as Error);\r\n }\r\n }, [key]);\r\n\r\n // Re-read value when key changes\r\n useEffect(() => {\r\n if (!isClient) {\r\n return;\r\n }\r\n\r\n try {\r\n const item = window.sessionStorage.getItem(key);\r\n if (item !== null) {\r\n setStoredValue(deserializerRef.current(item));\r\n } else {\r\n setStoredValue(resolveInitialValue(initialValueRef.current));\r\n }\r\n } catch {\r\n setStoredValue(resolveInitialValue(initialValueRef.current));\r\n }\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n }, [key]);\r\n\r\n return [storedValue, setValue, removeValue] as const;\r\n}\r\n"],"mappings":";AAAA,SAAS,aAAa,WAAW,QAAQ,gBAAgB;AA0CzD,SAAS,oBAAuB,cAAkC;AAChE,SAAO,OAAO,iBAAiB,aAC1B,aAAyB,IAC1B;AACN;AA0DO,SAAS,kBACd,KACA,cACA,UAAuC,CAAC,GACZ;AAC5B,QAAM;AAAA,IACJ,aAAa,KAAK;AAAA,IAClB,eAAe,KAAK;AAAA,IACpB;AAAA,EACF,IAAI;AAGJ,QAAM,gBAAgB,OAAO,UAAU;AACvC,QAAM,kBAAkB,OAAO,YAAY;AAC3C,QAAM,aAAa,OAAO,OAAO;AACjC,gBAAc,UAAU;AACxB,kBAAgB,UAAU;AAC1B,aAAW,UAAU;AAGrB,QAAM,kBAAkB,OAAO,YAAY;AAC3C,kBAAgB,UAAU;AAG1B,QAAM,WAAW,OAAO,WAAW;AAGnC,QAAM,CAAC,aAAa,cAAc,IAAI,SAAY,MAAM;AACtD,QAAI,CAAC,UAAU;AACb,aAAO,oBAAoB,YAAY;AAAA,IACzC;AAEA,QAAI;AACF,YAAM,OAAO,OAAO,eAAe,QAAQ,GAAG;AAC9C,UAAI,SAAS,MAAM;AACjB,eAAO,gBAAgB,QAAQ,IAAI;AAAA,MACrC;AACA,aAAO,oBAAoB,YAAY;AAAA,IACzC,SAAS,OAAO;AACd,iBAAW,UAAU,KAAc;AACnC,aAAO,oBAAoB,YAAY;AAAA,IACzC;AAAA,EACF,CAAC;AAGD,QAAM,iBAAiB,OAAU,WAAW;AAC5C,iBAAe,UAAU;AAGzB,QAAM,WAAW;AAAA,IACf,CAAC,UAAU;AACT,UAAI;AACF,cAAM,eAAe,eAAe;AACpC,cAAM,eACJ,iBAAiB,WAAW,MAAM,YAAY,IAAI;AAEpD,uBAAe,YAAY;AAE3B,YAAI,OAAO,WAAW,aAAa;AACjC,iBAAO,eAAe;AAAA,YACpB;AAAA,YACA,cAAc,QAAQ,YAAY;AAAA,UACpC;AAAA,QACF;AAAA,MACF,SAAS,OAAO;AACd,mBAAW,UAAU,KAAc;AAAA,MACrC;AAAA,IACF;AAAA,IACA,CAAC,GAAG;AAAA,EACN;AAGA,QAAM,cAAc,YAAY,MAAM;AACpC,QAAI;AACF,YAAM,UAAU,oBAAoB,gBAAgB,OAAO;AAC3D,qBAAe,OAAO;AAEtB,UAAI,OAAO,WAAW,aAAa;AACjC,eAAO,eAAe,WAAW,GAAG;AAAA,MACtC;AAAA,IACF,SAAS,OAAO;AACd,iBAAW,UAAU,KAAc;AAAA,IACrC;AAAA,EACF,GAAG,CAAC,GAAG,CAAC;AAGR,YAAU,MAAM;AACd,QAAI,CAAC,UAAU;AACb;AAAA,IACF;AAEA,QAAI;AACF,YAAM,OAAO,OAAO,eAAe,QAAQ,GAAG;AAC9C,UAAI,SAAS,MAAM;AACjB,uBAAe,gBAAgB,QAAQ,IAAI,CAAC;AAAA,MAC9C,OAAO;AACL,uBAAe,oBAAoB,gBAAgB,OAAO,CAAC;AAAA,MAC7D;AAAA,IACF,QAAQ;AACN,qBAAe,oBAAoB,gBAAgB,OAAO,CAAC;AAAA,IAC7D;AAAA,EAEF,GAAG,CAAC,GAAG,CAAC;AAER,SAAO,CAAC,aAAa,UAAU,WAAW;AAC5C;","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@usefy/use-session-storage",
|
|
3
|
+
"version": "0.0.2",
|
|
4
|
+
"description": "A React hook for persisting state in sessionStorage",
|
|
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
|
+
"@testing-library/user-event": "^14.6.1",
|
|
26
|
+
"@types/react": "^19.0.0",
|
|
27
|
+
"jsdom": "^27.3.0",
|
|
28
|
+
"react": "^19.0.0",
|
|
29
|
+
"rimraf": "^6.0.1",
|
|
30
|
+
"tsup": "^8.0.0",
|
|
31
|
+
"typescript": "^5.0.0",
|
|
32
|
+
"vitest": "^4.0.16"
|
|
33
|
+
},
|
|
34
|
+
"publishConfig": {
|
|
35
|
+
"access": "public"
|
|
36
|
+
},
|
|
37
|
+
"repository": {
|
|
38
|
+
"type": "git",
|
|
39
|
+
"url": "https://github.com/geon0529/usefy.git",
|
|
40
|
+
"directory": "packages/use-session-storage"
|
|
41
|
+
},
|
|
42
|
+
"license": "MIT",
|
|
43
|
+
"keywords": [
|
|
44
|
+
"react",
|
|
45
|
+
"hooks",
|
|
46
|
+
"session-storage",
|
|
47
|
+
"sessionStorage",
|
|
48
|
+
"state",
|
|
49
|
+
"persistence"
|
|
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
|
+
}
|