🐥
【TypeScript】ローカルストレージにデータを保持するカスタムフックの設計
概要
本記事では、TypeScriptを用いてデータの保存方法を抽象化し、SessionStorage、LocalStorage、Cookieの3つの異なるストレージに対応可能なStore Managerの実装方法について紹介します。また、Reactのカスタムフックを使って、これらのストレージと連携する方法も説明します。
Store Managerの実装
データの管理方法が変更されても対応できるように、共通のインターフェースStoreManager
を定義し、それぞれのストレージに対する具体的な実装を行います。
インターフェースとファクトリ関数の定義
// 共通のインターフェースにしてデータの管理方法変更時の開発工数を低減させる
interface StoreManager<T> {
get: () => T | null;
set: (value: T) => void;
}
interface StoreManagerConstructor {
<T>(key: string, options: { expires: number }): StoreManager<T>;
}
SessionStorageの実装
const createSessionStorageManager: StoreManagerConstructor = <T>(
key: string,
options: { expires: number }
) => {
const get = () => {
try {
const value = sessionStorage.getItem(key);
if (!value) {
return null;
}
const parsed = JSON.parse(value) as { value: T; createdAt: number };
if (parsed.createdAt + options.expires < Date.now()) {
return null;
}
return parsed.value;
} catch (error) {
console.error(error);
return null;
}
};
const set = (value: T) => {
sessionStorage.setItem(
key,
JSON.stringify({
value,
createdAt: Date.now(),
})
);
};
return { get, set };
};
LocalStorageの実装
const createLocalStorageManager: StoreManagerConstructor = <T>(
key: string,
options: { expires: number }
) => {
const get = () => {
try {
const value = localStorage.getItem(key);
if (!value) {
return null;
}
const parsed = JSON.parse(value) as { value: T; createdAt: number };
if (parsed.createdAt + options.expires < Date.now()) {
return null;
}
return parsed.value;
} catch (error) {
console.error(error);
return null;
}
};
const set = (value: T) => {
localStorage.setItem(
key,
JSON.stringify({
value,
createdAt: Date.now(),
})
);
};
return { get, set };
};
Cookieの実装
const createCookieManager: StoreManagerConstructor = <T>(
key: string,
options: { expires: number }
) => {
const get = () => {
try {
const value = document.cookie
.split('; ')
.find((item) => item.startsWith(`${key}=`));
if (!value) {
return null;
}
const parsedValue = JSON.parse(decodeURIComponent(value.split('=')[1])) as T;
return parsedValue;
} catch (error) {
console.error(error);
return null;
}
};
const set = (value: T) => {
const expiresDate = new Date(Date.now() + options.expires).toUTCString();
document.cookie = `${key}=${encodeURIComponent(JSON.stringify(value))}; expires=${expiresDate}; path=/`;
};
return { get, set };
};
Store Managerのファクトリ関数
export const StoreType = {
SESSION_STORAGE: 'sessionStorage',
LOCAL_STORAGE: 'localStorage',
COOKIE: 'cookie',
} as const;
type StoreType = (typeof StoreType)[keyof typeof StoreType];
export const createStoreManger = <T>(
type: StoreType,
key: string,
options: { expires: number }
) => {
switch (type) {
case StoreType.SESSION_STORAGE:
return createSessionStorageManager<T>(key, options);
case StoreType.LOCAL_STORAGE:
return createLocalStorageManager<T>(key, options);
case StoreType.COOKIE:
return createCookieManager<T>(key, options);
}
}
React Hooksによるカスタムフックの実装
カスタムフックuseStoreManager
を用いて、ストレージと連携するための簡便な方法を提供します。
カスタムフックの実装
const useStoreManger = <T>(...args: Parameters<typeof createStoreManger>) => {
const [value, _setValue] = useState<T | null>(null);
const manager = useMemo(() => {
return createStoreManger<T>(...args);
}, [...args]);
useEffect(() => {
_setValue((prev) => prev || manager.get());
}, [manager]);
const setValue = (value: T) => {
manager.set(value);
_setValue(value);
};
return [value, setValue] as const;
};
カスタムフックの使用例
それぞれのストレージタイプに対する使用例を以下に示します。
SessionStorageの使用例
const useSample1 = () => {
const [value, setValue] = useStoreManger<{ userName: string }>(
StoreType.SESSION_STORAGE,
'user',
{
expires: 1000 * 60 * 60 * 24, // 1日
}
);
// いろんな処理...
return { value, setValue };
};
LocalStorageの使用例
const useSample2 = () => {
const [value, setValue] = useStoreManger<{ userName: string }>(
StoreType.LOCAL_STORAGE,
'user',
{
expires: 1000 * 60 * 60 * 24, // 1日
}
);
// いろんな処理...
return { value, setValue };
};
Cookieの使用例
const useSample3 = () => {
const [value, setValue] = useStoreManger<{ userName: string }>(
StoreType.COOKIE,
'user',
{
expires: 1000 * 60 * 60 * 24, // 1日
}
);
// いろんな処理...
return { value, setValue };
};
まとめ
今回の実装では、データの保存方法を抽象化することで、将来的な変更にも対応可能な設計を行いました。また、Reactのカスタムフックを用いることで、ストレージとの連携が簡単に行えるようにしました。この記事が、皆さんの開発に役立つことを願っています。
Discussion