Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: version switch #87

Merged
merged 1 commit into from
Jun 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 22 additions & 52 deletions src/hooks/useQueryState.ts
Original file line number Diff line number Diff line change
@@ -1,64 +1,34 @@
import { useState, useEffect } from 'react';
import { useRouter } from 'next/router';
import { useState, useEffect, useCallback } from 'react';

type QueryValue = string | boolean | number;

function useQueryState<T extends QueryValue>(
key: string,
defaultValue: T,
ignoreValues: QueryValue[] = []
): [T, (newValue: T) => void] {
const router = useRouter();
const [state, setState] = useState<T>(() => {
const valueFromQuery = router.isReady ? router.query[key] : undefined;
return parseValue(valueFromQuery, defaultValue);
function useQueryState(key: string, defaultValue: string): [string, (value: string) => void] {
const [value, setValue] = useState(() => {
const params = new URLSearchParams(window.location.search);
return params.get(key) || defaultValue;
});

useEffect(() => {
if (!router.isReady) return;
const valueFromQuery = router.query[key];
setState(parseValue(valueFromQuery, defaultValue));
}, [router.isReady, router.query, key, defaultValue]);

useEffect(() => {
const handleRouteChange = () => {
const valueFromQuery = router.query[key];
setState(parseValue(valueFromQuery, defaultValue));
};

router.events.on('routeChangeComplete', handleRouteChange);
return () => {
router.events.off('routeChangeComplete', handleRouteChange);
};
}, [router.events, key, defaultValue]);

const setQueryState = (newValue: T) => {
const newQuery = { ...router.query };
const updateUrl = useCallback((newValue: string | null) => {
const url = new URL(window.location.href);
const params = new URLSearchParams(url.search);

if (ignoreValues.includes(newValue) || newValue === defaultValue) {
delete newQuery[key];
if (newValue === defaultValue || newValue == null) {
params.delete(key);
} else {
newQuery[key] = stringifyValue(newValue) as string;
params.set(key, newValue);
}

router.push({ pathname: router.pathname, query: newQuery }, undefined, { shallow: true });
setState(newValue);
};
// 历史状态管理,避免生成新的历史记录条目
window.history.replaceState({}, '', `${url.pathname}?${params}`);
}, [key, defaultValue]);

return [state, setQueryState];
}

function parseValue<T extends QueryValue>(value: string | string[] | undefined, defaultValue: T): T {
if (value === undefined || Array.isArray(value)) return defaultValue;
if (value === 'true') return true as T;
if (value === 'false') return false as T;
if (!isNaN(Number(value))) return Number(value) as T;
return value as unknown as T;
}
useEffect(() => {
if (value !== defaultValue) {
updateUrl(value);
} else {
updateUrl(null); // 传 null 以移除查询参数
}
}, [value, updateUrl, defaultValue]);

function stringifyValue(value: QueryValue): string {
if (typeof value === 'boolean' || typeof value === 'number') return String(value);
return value as string;
return [value as string, setValue];
}

export default useQueryState;
2 changes: 1 addition & 1 deletion src/slugs/trends/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const MAX_COUNT = 5;

export default function Trends({ manifest: pkg }: PageProps) {
const [search, setSearch] = useState('');
const [vs, setVS] = useQueryState<string>('vs', '');
const [vs, setVS] = useQueryState('vs', '');
const [pkgs, setPkgs] = useState<string[]>(vs ? vs.split(',').slice(0, MAX_COUNT) : [pkg.name]);

const { data: searchResult, isLoading } = useCachedSearch({
Expand Down
8 changes: 4 additions & 4 deletions src/slugs/versions/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ const useStyles = createStyles(({ token, css }) => {

function TagsList({ tagsInfo, pkg }: { tagsInfo: Record<string, string[]>; pkg: PackageManifest }) {
const { styles } = useStyles();
const [type, setTags] = useQueryState<string>('tags', 'prod', ['prod']);
const [type, setTags] = useQueryState('tags', 'prod');
const onlyProd = type === 'prod';
return (
<div style={{ position: 'relative' }}>
Expand All @@ -71,7 +71,7 @@ function TagsList({ tagsInfo, pkg }: { tagsInfo: Record<string, string[]>; pkg:
}}
>
<Segmented
defaultValue={type || 'prod'}
value={type}
options={[
{ label: '正式版本', value: 'prod' },
{ label: '所有版本', value: 'all' },
Expand Down Expand Up @@ -108,7 +108,7 @@ function TagsList({ tagsInfo, pkg }: { tagsInfo: Record<string, string[]>; pkg:

function VersionsList({ versions, pkg }: { versions: NpmPackageVersion[]; pkg: PackageManifest }) {
const { styles } = useStyles();
const [type, setVersions] = useQueryState<string>('versions', 'prod', ['prod']);
const [type, setVersions] = useQueryState('versions', 'prod');
const onlyProd = type === 'prod';
return (
<div style={{ position: 'relative' }}>
Expand All @@ -131,7 +131,7 @@ function VersionsList({ versions, pkg }: { versions: NpmPackageVersion[]; pkg: P
}}
>
<Segmented
defaultValue={type || 'prod'}
value={type}
options={[
{ label: '正式版本', value: 'prod' },
{ label: '所有版本', value: 'all' },
Expand Down
Loading