39 lines
1.2 KiB
TypeScript
39 lines
1.2 KiB
TypeScript
import { useEffect, useState } from 'react';
|
||
|
||
/**
|
||
* 把模块内子 tab 状态同步到 URL hash 二级段。
|
||
* hash 形如 `#<moduleId>`(= 默认 sub)或 `#<moduleId>/<sub>`。
|
||
* 默认值不写入 hash,刷新页面可恢复。
|
||
*/
|
||
export function useHashSubTab<T extends string>(
|
||
moduleId: string,
|
||
subs: readonly T[],
|
||
): [T, (sub: T) => void] {
|
||
const defaultSub = subs[0];
|
||
|
||
const parse = (): T => {
|
||
const hash = window.location.hash.slice(1);
|
||
const [first, second] = hash.split('/');
|
||
if (first !== moduleId) return defaultSub;
|
||
if (second && (subs as readonly string[]).includes(second)) return second as T;
|
||
return defaultSub;
|
||
};
|
||
|
||
const [sub, setSubState] = useState<T>(parse);
|
||
|
||
useEffect(() => {
|
||
const onChange = () => setSubState(parse());
|
||
window.addEventListener('hashchange', onChange);
|
||
return () => window.removeEventListener('hashchange', onChange);
|
||
}, [moduleId]);
|
||
|
||
const setSub = (next: T) => {
|
||
const { pathname, search } = window.location;
|
||
const newHash = next === defaultSub ? `#${moduleId}` : `#${moduleId}/${next}`;
|
||
window.history.replaceState(null, '', `${pathname}${search}${newHash}`);
|
||
setSubState(next);
|
||
};
|
||
|
||
return [sub, setSub];
|
||
}
|