Skip to content

Commit

Permalink
Make params consistent for string paths
Browse files Browse the repository at this point in the history
  • Loading branch information
JonahPlusPlus authored and molefrog committed May 30, 2024
1 parent 3e96871 commit af81d14
Show file tree
Hide file tree
Showing 9 changed files with 150 additions and 50 deletions.
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -318,12 +318,15 @@ const User = () => {
const params = useParams();

params.id; // "1"

// alternatively, use the index to access the prop
params[0]; // "1"
};

<Route path="/user/:id" component={User}> />
```

For regex paths, parameters are accessible as indices or through their group name.
It is the same for regex paths. Capture groups can be accessed by their index, or if there is a named capture group, that can be used instead.

```js
import { Route, useParams } from "wouter";
Expand Down
11 changes: 7 additions & 4 deletions packages/wouter-preact/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ export * from "./router.js";

import { RouteParams } from "regexparam";

export type StringRouteParams<T extends string> = RouteParams<T> & {
[param: number]: string | undefined;
};
export type RegexRouteParams = { [key: string | number]: string | undefined };

/**
Expand Down Expand Up @@ -67,7 +70,7 @@ export interface RouteProps<
params: T extends DefaultParams
? T
: RoutePath extends string
? RouteParams<RoutePath>
? StringRouteParams<RoutePath>
: RegexRouteParams
) => ComponentChildren)
| ComponentChildren;
Expand All @@ -77,7 +80,7 @@ export interface RouteProps<
T extends DefaultParams
? T
: RoutePath extends string
? RouteParams<RoutePath>
? StringRouteParams<RoutePath>
: RegexRouteParams
>
>;
Expand Down Expand Up @@ -163,7 +166,7 @@ export function useRoute<
T extends DefaultParams
? T
: RoutePath extends string
? RouteParams<RoutePath>
? StringRouteParams<RoutePath>
: RegexRouteParams
>;

Expand All @@ -176,7 +179,7 @@ export function useSearch<
>(): ReturnType<H>;

export function useParams<T = undefined>(): T extends string
? RouteParams<T>
? StringRouteParams<T>
: T extends undefined
? DefaultParams
: T;
Expand Down
18 changes: 10 additions & 8 deletions packages/wouter/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,20 +99,22 @@ const matchRoute = (parser, route, path, loose) => {
true,

(() => {
/// for regex paths, `keys` will always be false
if (keys !== false) {
// an object with parameters matched, e.g. { foo: "bar" } for "/:foo"
// we "zip" two arrays here to construct the object
// ["foo"], ["bar"] → { foo: "bar" }
return Object.fromEntries(keys.map((key, i) => [key, matches[i]]));
}
// for regex paths, `keys` will always be false

// an object with parameters matched, e.g. { foo: "bar" } for "/:foo"
// we "zip" two arrays here to construct the object
// ["foo"], ["bar"] → { foo: "bar" }
const groups =
keys !== false
? Object.fromEntries(keys.map((key, i) => [key, matches[i]]))
: result.groups;

// convert the array to an instance of object
// this makes it easier to integrate with the existing param implementation
let obj = { ...matches };

// merge named capture groups with matches array
result.groups && Object.assign(obj, result.groups);
groups && Object.assign(obj, groups);

return obj;
})(),
Expand Down
4 changes: 2 additions & 2 deletions packages/wouter/test/parser.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { Router, useRouter, useRoute, Parser } from "wouter";
import { memoryLocation } from "wouter/memory-location";

// Custom parser that uses `path-to-regexp` instead of `regexparam`
const pathToRegexpParser: Parser = (route: string | RegExp) => {
const pathToRegexpParser: Parser = (route: string) => {
const keys: Key[] = [];
const pattern = pathToRegexp(route, keys);

Expand Down Expand Up @@ -42,6 +42,6 @@ it("allows to change the behaviour of route matching", () => {

expect(result.current).toStrictEqual([
true,
{ pages: undefined, rest: "10/bio", 0: "home" },
{ 0: "home", 1: undefined, 2: "10/bio", pages: undefined, rest: "10/bio" },
]);
});
8 changes: 7 additions & 1 deletion packages/wouter/test/use-params.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,18 @@ it("returns an object with arbitrary parameters", () => {

expectTypeOf(params).toBeObject();
expectTypeOf(params.any).toEqualTypeOf<string | undefined>();
expectTypeOf(params[0]).toEqualTypeOf<string | undefined>();
});

it("can infer the type of parameters from the route path", () => {
const params = useParams<"/app/users/:name?/:id">();

expectTypeOf(params).toMatchTypeOf<{ id: string; name?: string }>();
expectTypeOf(params).toMatchTypeOf<{
0?: string;
1?: string;
id: string;
name?: string;
}>();
});

it("can accept the custom type of parameters as a generic argument", () => {
Expand Down
33 changes: 28 additions & 5 deletions packages/wouter/test/use-params.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ it("contains a * parameter when used inside an empty <Route />", () => {
),
});

expect(result.current).toEqual({ "*": "app-2/goods/tees" });
expect(result.current).toEqual({
0: "app-2/goods/tees",
"*": "app-2/goods/tees",
});
});

it("returns an empty object when there are no params", () => {
Expand All @@ -40,7 +43,12 @@ it("returns parameters from the closest parent <Route /> match", () => {
),
});

expect(result.current).toEqual({ id: "1", name: "maria" });
expect(result.current).toEqual({
0: "1",
1: "maria",
id: "1",
name: "maria",
});
});

it("rerenders with parameters change", () => {
Expand All @@ -57,10 +65,20 @@ it("rerenders with parameters change", () => {
expect(result.current).toBeNull();

act(() => navigate("/posts/all"));
expect(result.current).toEqual({ a: "posts", b: "all" });
expect(result.current).toEqual({
0: "posts",
1: "all",
a: "posts",
b: "all",
});

act(() => navigate("/posts/latest"));
expect(result.current).toEqual({ a: "posts", b: "latest" });
expect(result.current).toEqual({
0: "posts",
1: "latest",
a: "posts",
b: "latest",
});
});

it("extracts parameters of the nested route", () => {
Expand All @@ -79,5 +97,10 @@ it("extracts parameters of the nested route", () => {
),
});

expect(result.current).toEqual({ version: "v2", chain: "eth" });
expect(result.current).toEqual({
0: "v2",
1: "eth",
version: "v2",
chain: "eth",
});
});
3 changes: 3 additions & 0 deletions packages/wouter/test/use-route.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ it("infers parameters from the route path", () => {

if (inferedParams) {
expectTypeOf(inferedParams).toMatchTypeOf<{
0?: string;
1?: string;
2?: string;
name?: string;
id: string;
wildcard?: string;
Expand Down
Loading

0 comments on commit af81d14

Please sign in to comment.