Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 4x 4x 4x 2x 1x 1x 4x 4x 1x 1x 4x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 4x 4x 4x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x | import { hashKey } from '@tanstack/react-query';
import { useCallback, useEffect, useRef, useState, type Dispatch, type SetStateAction } from 'react';
import { type ZodType } from 'zod';
import { useUpdateEffect } from '../use-update-effect/useUpdateEffect';
const isStateFn = <TState>(setStateAction: unknown): setStateAction is (prevState?: TState) => TState =>
typeof setStateAction === 'function';
// Helper function to get a value from storage and validate it against a schema.
const getFromStorage = <TState>(key: string, storage: Storage, validationSchema?: ZodType<TState>) => {
const storageState = storage.getItem(key);
try {
return storageState
? validationSchema
? validationSchema.parse(JSON.parse(storageState))
: (JSON.parse(storageState) as TState)
: undefined;
} catch {
return undefined;
}
};
export function useStateInStorage<TState>(
key: unknown[] | string,
initialState: TState | ((stateFromStorage?: TState) => TState),
options?: {
storage?: Storage;
validationSchema?: ZodType<TState>;
},
): [state: TState, setState: Dispatch<SetStateAction<TState>>, clear: () => void];
export function useStateInStorage<TState = undefined>(
key: unknown[] | string,
initialState?: undefined,
options?: {
storage?: Storage;
validationSchema?: ZodType<TState>;
},
): [state: TState | undefined, setState: Dispatch<SetStateAction<TState | undefined>>, clear: () => void];
export function useStateInStorage<TState>(
/** Key in storage. Will be hashed using react-query's hashing method. */
key: unknown[] | string,
/** Initial state. If there is a value in storage, it is ignored. You can pass a function to get the value from storage (if any) and return the computed state. */
initialState?: TState | ((statementFromStorage?: TState) => TState),
{
storage = localStorage,
validationSchema,
}: {
/** Override storage (defaults to localStorage). */
storage?: Storage;
/**
* Optional validation schema. If the value in storage does not match the schema, it will be ignored.
* You can use this to invalidate the storage after a schema change.
*/
validationSchema?: ZodType<TState>;
} = {},
): [state: TState | undefined, setState: Dispatch<SetStateAction<TState | undefined>>, clear: () => void] {
const hashedKey = hashKey(Array.isArray(key) ? key : [key]);
const initialStateRef = useRef(initialState);
initialStateRef.current = initialState;
const validationSchemaRef = useRef(validationSchema);
validationSchemaRef.current = validationSchema;
const [state, setState] = useState(() => {
// Get the state from storage if any. If it doesn't match the schema, it will be undefined (as if there was no value).
const stateInStorage = getFromStorage(hashedKey, storage, validationSchema);
return isStateFn<TState>(initialState) ? initialState(stateInStorage) : (stateInStorage ?? initialState);
});
useUpdateEffect(() => {
// Get the state from storage if any. If it doesn't match the schema, it will be undefined (as if there was no value).
const stateInStorage = getFromStorage(hashedKey, storage, validationSchemaRef.current);
setState(
isStateFn<TState>(initialStateRef.current)
? initialStateRef.current(stateInStorage)
: (stateInStorage ?? initialStateRef.current),
);
}, [hashedKey, storage]);
// When the state changes, persist it in local storage.
useEffect(() => {
storage.setItem(hashedKey, JSON.stringify(state));
}, [state, hashedKey, storage]);
const handleClear = useCallback(() => {
storage.removeItem(hashedKey);
}, [hashedKey, storage]);
return [state, setState, handleClear];
}
|