diff --git a/examples/store/app/admin/pages/admin/products/[id].tsx b/examples/store/app/admin/pages/admin/products/[id].tsx index 59d933a0a2..f12dc214ba 100644 --- a/examples/store/app/admin/pages/admin/products/[id].tsx +++ b/examples/store/app/admin/pages/admin/products/[id].tsx @@ -1,12 +1,12 @@ import { Suspense } from "react" -import { Link, useRouter, useQuery } from "blitz" +import { Link, useRouter, useQuery, useParams } from "blitz" import getProduct from "app/products/queries/getProduct" import ProductForm from "app/products/components/ProductForm" function Product() { const router = useRouter() - const id = parseInt(router?.query.id as string) - const [product, { mutate }] = useQuery(getProduct, { where: { id } }) + const { id } = useParams() + const [product, { mutate }] = useQuery(getProduct, { where: { id: Number(id) } }) return ( diff --git a/examples/store/cypress/integration/admin/products/index.test.ts b/examples/store/cypress/integration/admin/products/index.test.ts index 2a24c3391a..fcb978f9ea 100644 --- a/examples/store/cypress/integration/admin/products/index.test.ts +++ b/examples/store/cypress/integration/admin/products/index.test.ts @@ -19,6 +19,17 @@ describe("admin/products page", () => { cy.location("pathname").should("equal", "/admin") }) + it("shows ascending order", () => { + cy.get("ul > li") + .first() + .then(($li) => { + const title = $li.find("> a").text() + + cy.visit("/admin/products?order=asc") + cy.get("ul > li").last().contains("a", title) + }) + }) + // This is kind of redundant because this logic is handled in insert() it("shows latest created product", () => { const name = insert() diff --git a/packages/core/package.json b/packages/core/package.json index 6a298f6ebc..5f00d80e77 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -46,7 +46,8 @@ "dependencies": { "pretty-ms": "6.0.1", "react-query": "1.3.3", - "serialize-error": "6.0.0" + "serialize-error": "6.0.0", + "url": "0.11.0" }, "devDependencies": { "@rollup/pluginutils": "3.0.8", diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index d7536cdfda..9d13c1f4cd 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -1,5 +1,7 @@ export * from './use-query' export * from './use-paginated-query' +export * from './use-params' +export * from './use-router-query' export * from './use-infinite-query' export * from './ssr-query' export * from './rpc' diff --git a/packages/core/src/use-params.ts b/packages/core/src/use-params.ts new file mode 100644 index 0000000000..b0c3fed39d --- /dev/null +++ b/packages/core/src/use-params.ts @@ -0,0 +1,46 @@ +import {useRouter} from 'next/router' +import {useRouterQuery} from './use-router-query' + +type ParsedUrlQueryValue = string | string[] | undefined + +export interface ParsedUrlQuery { + [key: string]: ParsedUrlQueryValue +} + +function areQueryValuesEqual(value1: ParsedUrlQueryValue, value2: ParsedUrlQueryValue) { + // Check if their type match + if (typeof value1 !== typeof value2) { + return false + } + + if (Array.isArray(value1) && Array.isArray(value2)) { + if (value1.length !== value2.length) { + return false + } + + for (let i = 0; i < value1.length; i++) { + if (value1[i] !== value2[i]) { + return false + } + } + + return true + } + + return value1 === value2 +} + +export function extractRouterParams(routerQuery: ParsedUrlQuery, query: ParsedUrlQuery) { + return Object.fromEntries( + Object.entries(routerQuery).filter( + ([key, value]) => typeof query[key] === 'undefined' || !areQueryValuesEqual(value, query[key]), + ), + ) +} + +export function useParams() { + const router = useRouter() + const query = useRouterQuery() + + return extractRouterParams(router.query, query) +} diff --git a/packages/core/src/use-router-query.ts b/packages/core/src/use-router-query.ts new file mode 100644 index 0000000000..8c76966a0f --- /dev/null +++ b/packages/core/src/use-router-query.ts @@ -0,0 +1,8 @@ +import {useRouter} from 'next/router' +import {parse} from 'url' + +export function useRouterQuery() { + const router = useRouter() + const {query} = parse(router.asPath, true) + return query +} diff --git a/packages/core/test/use-params.test.ts b/packages/core/test/use-params.test.ts new file mode 100644 index 0000000000..5fcb93eaba --- /dev/null +++ b/packages/core/test/use-params.test.ts @@ -0,0 +1,30 @@ +import {extractRouterParams} from '@blitzjs/core' + +describe('useParams', () => { + describe('extractRouterParams', () => { + it('returns proper params', () => { + const routerQuery = { + id: '1', + cat: 'category', + slug: ['example', 'multiple', 'slugs'], + empty: '', + queryArray: ['1', '123', ''], + } + + const query = { + cat: 'somethingelse', + slug: ['query-slug'], + queryArray: ['1', '123', ''], + onlyInQuery: 'onlyInQuery', + } + + const params = extractRouterParams(routerQuery, query) + expect(params).toEqual({ + id: '1', + cat: 'category', + slug: ['example', 'multiple', 'slugs'], + empty: '', + }) + }) + }) +})