From 0d593bc450a8ceb8d2f6a1dd90571a952d958c3e Mon Sep 17 00:00:00 2001 From: "peirong.wu" Date: Mon, 24 Oct 2022 15:36:16 +0800 Subject: [PATCH] [resumes][feat] adapt useSearchParams --- apps/portal/src/pages/resumes/browse.tsx | 82 +++++++++++++++++-- .../src/utils/resumes/useSearchParams.ts | 26 ++++++ 2 files changed, 99 insertions(+), 9 deletions(-) create mode 100644 apps/portal/src/utils/resumes/useSearchParams.ts diff --git a/apps/portal/src/pages/resumes/browse.tsx b/apps/portal/src/pages/resumes/browse.tsx index c7b4fe93..e27bf0eb 100644 --- a/apps/portal/src/pages/resumes/browse.tsx +++ b/apps/portal/src/pages/resumes/browse.tsx @@ -1,7 +1,7 @@ import Head from 'next/head'; -import { useRouter } from 'next/router'; +import Router, { useRouter } from 'next/router'; import { useSession } from 'next-auth/react'; -import { Fragment, useEffect, useState } from 'react'; +import { Fragment, useEffect, useMemo, useState } from 'react'; import { Dialog, Disclosure, Transition } from '@headlessui/react'; import { FunnelIcon, MinusIcon, PlusIcon } from '@heroicons/react/20/solid'; import { @@ -39,6 +39,7 @@ import ResumeListItems from '~/components/resumes/browse/ResumeListItems'; import ResumeSignInButton from '~/components/resumes/shared/ResumeSignInButton'; import useDebounceValue from '~/utils/resumes/useDebounceValue'; +import useSearchParams from '~/utils/resumes/useSearchParams'; import { trpc } from '~/utils/trpc'; import type { FilterState } from '../../components/resumes/browse/resumeFilters'; @@ -101,19 +102,82 @@ const getEmptyDataText = ( export default function ResumeHomePage() { const { data: sessionData } = useSession(); const router = useRouter(); - const [tabsValue, setTabsValue] = useState(BROWSE_TABS_VALUES.ALL); - const [sortOrder, setSortOrder] = useState('latest'); - const [searchValue, setSearchValue] = useState(''); - const [userFilters, setUserFilters] = useState(INITIAL_FILTER_STATE); - const [shortcutSelected, setShortcutSelected] = useState('All'); - const [currentPage, setCurrentPage] = useState(1); + const [tabsValue, setTabsValue, isTabsValueInit] = useSearchParams( + 'tabsValue', + BROWSE_TABS_VALUES.ALL, + ); + const [sortOrder, setSortOrder, isSortOrderInit] = useSearchParams( + 'sortOrder', + 'latest', + ); + const [searchValue, setSearchValue, isSearchValueInit] = useSearchParams( + 'searchValue', + '', + ); + const [shortcutSelected, setShortcutSelected, isShortcutInit] = + useSearchParams('shortcutSelected', 'All'); + const [currentPage, setCurrentPage, isCurrentPageInit] = useSearchParams( + 'currentPage', + 1, + ); + const [userFilters, setUserFilters, isUserFiltersInit] = useSearchParams( + 'userFilters', + INITIAL_FILTER_STATE, + ); const [mobileFiltersOpen, setMobileFiltersOpen] = useState(false); const skip = (currentPage - 1) * PAGE_LIMIT; + const isSearchOptionsInit = useMemo(() => { + return ( + isTabsValueInit && + isSortOrderInit && + isSearchValueInit && + isShortcutInit && + isCurrentPageInit && + isUserFiltersInit + ); + }, [ + isTabsValueInit, + isSortOrderInit, + isSearchValueInit, + isShortcutInit, + isCurrentPageInit, + isUserFiltersInit, + ]); useEffect(() => { setCurrentPage(1); - }, [userFilters, sortOrder, searchValue]); + }, [userFilters, sortOrder, setCurrentPage, searchValue]); + + useEffect(() => { + // Router.replace used instead of router.replace to avoid + // the page reloading itself since the router.replace + // callback changes on every page load + if (!isSearchOptionsInit) { + return; + } + + Router.replace({ + pathname: router.pathname, + query: { + currentPage: JSON.stringify(currentPage), + searchValue: JSON.stringify(searchValue), + shortcutSelected: JSON.stringify(shortcutSelected), + sortOrder: JSON.stringify(sortOrder), + tabsValue: JSON.stringify(tabsValue), + userFilters: JSON.stringify(userFilters), + }, + }); + }, [ + tabsValue, + sortOrder, + searchValue, + userFilters, + shortcutSelected, + currentPage, + router.pathname, + isSearchOptionsInit, + ]); const allResumesQuery = trpc.useQuery( [ diff --git a/apps/portal/src/utils/resumes/useSearchParams.ts b/apps/portal/src/utils/resumes/useSearchParams.ts new file mode 100644 index 00000000..0bea502c --- /dev/null +++ b/apps/portal/src/utils/resumes/useSearchParams.ts @@ -0,0 +1,26 @@ +import { useRouter } from 'next/router'; +import { useEffect, useState } from 'react'; + +export const useSearchParams = (name: string, defaultValue: T) => { + const [isInitialized, setIsInitialized] = useState(false); + const router = useRouter(); + + const [filters, setFilters] = useState(defaultValue); + + useEffect(() => { + if (router.isReady && !isInitialized) { + // Initialize from url query params + const query = router.query[name]; + if (query) { + const parsedQuery = + typeof query === 'string' ? JSON.parse(query) : query; + setFilters(parsedQuery); + } + setIsInitialized(true); + } + }, [isInitialized, name, router]); + + return [filters, setFilters, isInitialized] as const; +}; + +export default useSearchParams;