From 6af347660444af9763bc703d327b5256939c29cc Mon Sep 17 00:00:00 2001 From: elrrrrrrr Date: Mon, 18 Dec 2023 21:22:04 +0800 Subject: [PATCH] feat: versions query (#65) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit > 对 versions 页面添加 tags、versions query 参数, closes #63 * 添加 `useQueryState` hook,同步 state 和 query 参数 * 支持 {prod, all},默认为 prod 且隐藏对应参数 --- src/hooks/useQueryState.ts | 64 ++++++++++++++++++++++++++++++++++++ src/slugs/versions/index.tsx | 13 +++++--- 2 files changed, 73 insertions(+), 4 deletions(-) create mode 100644 src/hooks/useQueryState.ts diff --git a/src/hooks/useQueryState.ts b/src/hooks/useQueryState.ts new file mode 100644 index 0000000..361bf57 --- /dev/null +++ b/src/hooks/useQueryState.ts @@ -0,0 +1,64 @@ +import { useState, useEffect } from 'react'; +import { useRouter } from 'next/router'; + +type QueryValue = string | boolean | number; + +function useQueryState( + key: string, + defaultValue: T, + ignoreValues: QueryValue[] = [] +): [T, (newValue: T) => void] { + const router = useRouter(); + const [state, setState] = useState(() => { + const valueFromQuery = router.isReady ? router.query[key] : undefined; + return parseValue(valueFromQuery, 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 }; + + if (ignoreValues.includes(newValue) || newValue === defaultValue) { + delete newQuery[key]; + } else { + newQuery[key] = stringifyValue(newValue) as string; + } + + router.push({ pathname: router.pathname, query: newQuery }, undefined, { shallow: true }); + setState(newValue); + }; + + return [state, setQueryState]; +} + +function parseValue(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; +} + +function stringifyValue(value: QueryValue): string { + if (typeof value === 'boolean' || typeof value === 'number') return String(value); + return value as string; +} + +export default useQueryState; diff --git a/src/slugs/versions/index.tsx b/src/slugs/versions/index.tsx index f8ca9a1..3fb24ad 100644 --- a/src/slugs/versions/index.tsx +++ b/src/slugs/versions/index.tsx @@ -17,6 +17,7 @@ import SyncAlert from '@/components/SyncAlert'; import { PageProps } from '@/pages/package/[...slug]'; import { createStyles } from 'antd-style'; import VersionTags from '@/components/VersionTags'; +import useQueryState from '@/hooks/useQueryState'; const useStyles = createStyles(({ token, css }) => { return { @@ -47,7 +48,8 @@ const useStyles = createStyles(({ token, css }) => { function TagsList({ tagsInfo, pkg }: { tagsInfo: Record; pkg: PackageManifest }) { const { styles } = useStyles(); - const [onlyProd, setOnlyProd] = React.useState(true); + const [type, setTags] = useQueryState('tags', 'prod', ['prod']); + const onlyProd = type === 'prod'; return (
; pkg: }} > setOnlyProd(v === 'prod')} + onChange={(v) => setTags(v as string)} />
    @@ -105,7 +108,8 @@ function TagsList({ tagsInfo, pkg }: { tagsInfo: Record; pkg: function VersionsList({ versions, pkg }: { versions: NpmPackageVersion[]; pkg: PackageManifest }) { const { styles } = useStyles(); - const [onlyProd, setOnlyProd] = React.useState(true); + const [type, setVersions] = useQueryState('versions', 'prod', ['prod']); + const onlyProd = type === 'prod'; return (
    setOnlyProd(v === 'prod')} + onChange={(v) => setVersions(v as string)} />