Skip to content

Commit

Permalink
Generic state type parameter (#9)
Browse files Browse the repository at this point in the history
* Added generic parameter for state type

* Added missing Generic type

* Added generic type parameter to the hooks and some examples

* docs(changeset): Added generic type parameter to the useSearchParamsState hooks
  • Loading branch information
bring-shrubbery authored Nov 12, 2023
1 parent 4695758 commit c6f9574
Show file tree
Hide file tree
Showing 11 changed files with 243 additions and 228 deletions.
6 changes: 6 additions & 0 deletions .changeset/tame-vans-retire.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@use-search-params-state/react": patch
"@use-search-params-state/next": patch
---

Added generic type parameter to the useSearchParamsState hooks
2 changes: 1 addition & 1 deletion apps/playground/src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { redirect } from "next/navigation";

export default function IndexPage() {
redirect("/default");
redirect("/basic");
}
3 changes: 3 additions & 0 deletions apps/playground/src/app/sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ export const Sidebar = () => {
With default values
</SidebarLink>
<SidebarLink href="/with-zod">With Zod</SidebarLink>
<SidebarLink href="/with-weak-typesafety">
With weak type safety
</SidebarLink>
</ul>
</div>
</aside>
Expand Down
35 changes: 35 additions & 0 deletions apps/playground/src/app/with-weak-typesafety/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
"use client";

import { SetKeyValueInputs } from "@/components/set-key-value-inputs";
import { Alert } from "@/components/ui/alert";

import { useSearchParamsState } from "@use-search-params-state/next";

interface SearchParamsType {
page: string;
search?: string;
}

export default function Page() {
const [state, setState] = useSearchParamsState<SearchParamsType>({
defaultValues: {
page: "1",
},
});

return (
<div>
<Alert>
{
"Check source code for this page. It has a generic type parameter passed to useSearchParamsState and enforces usage of the state inline with the provided type."
}
</Alert>

<SetKeyValueInputs
setState={(k, v) => setState(k as keyof SearchParamsType, v)}
/>

<pre>{JSON.stringify(state, null, 2)}</pre>
</div>
);
}
2 changes: 1 addition & 1 deletion apps/playground/src/app/with-zod/page.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export default function Page() {
return <div>with zod</div>;
return <div>TODO: with zod</div>;
}
18 changes: 18 additions & 0 deletions packages/next/src/example.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { useSearchParamsState } from "./index";

const Component = () => {
const [state, setState] = useSearchParamsState<{
greeting?: string;
hello: string;
}>();

const handleClick = () => {
state.greeting === "hello"
? setState("greeting", "world")
: setState("greeting", "hello");
};

return <button onClick={handleClick}>{state.greeting ?? "hello"}</button>;
};

<Component />;
10 changes: 8 additions & 2 deletions packages/next/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@ import { usePathname, useRouter, useSearchParams } from "next/navigation";
import { useSearchParamsState as useSearchParamsStateReact } from "@use-search-params-state/react";
import type { UseSearchParamsStateOptions } from "@use-search-params-state/react";

export const useSearchParamsState = (opts?: UseSearchParamsStateOptions) => {
export const useSearchParamsState = <
State extends
| { [K in keyof State]: string }
| Record<string, string> = Record<string, string>,
>(
opts?: UseSearchParamsStateOptions<State>,
) => {
const searchParams = useSearchParams();
const router = useRouter();
const pathname = usePathname();
Expand All @@ -12,7 +18,7 @@ export const useSearchParamsState = (opts?: UseSearchParamsStateOptions) => {
router.push(pathname + "?" + newSearchParams.toString());
};

return useSearchParamsStateReact({
return useSearchParamsStateReact<State>({
searchParams,
setSearchParams,
...opts,
Expand Down
54 changes: 54 additions & 0 deletions packages/react/src/example.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { useState } from "react";

import { useSearchParamsState } from "./index";

const Component1 = () => {
const [searchParams, setSearchParams] = useState(new URLSearchParams());

const [state, setState] = useSearchParamsState<{
greeting?: string;
hello: string;
}>({
searchParams,
setSearchParams: (newSearchParams) => {
console.log("newSearchParams", newSearchParams);
setSearchParams(newSearchParams);
},
});

const handleClick = () => {
state.greeting === "hello"
? setState("greeting", "world")
: setState("greeting", "hello");
};

return <button onClick={handleClick}>{state.greeting ?? "hello"}</button>;
};

<Component1 />;

const Comp2 = () => {
const [searchParams, setSearchParams] = useState(new URLSearchParams());

const [state, setState] = useSearchParamsState<{
page: string;
hello: string;
}>({
defaultValues: {
hello: "world",
},
searchParams,
setSearchParams: (newSearchParams) => {
console.log("newSearchParams", newSearchParams);
setSearchParams(newSearchParams);
},
});

const handleClick = () => {
state.page === "1" ? setState("page", "2") : setState("page", "1");
};

return <button onClick={handleClick}>{state.page}</button>;
};

<Comp2 />;
Loading

0 comments on commit c6f9574

Please sign in to comment.