T
TanStackโ€ข10mo ago
blank-aquamarine

Need Help Getting Started with TanStack

Hello,
I'm new to TanStack and I would like some clarification on an issue I encountered I followed the getting started guide in the TanStack documentation at the following link: https://tanstack.com/router/latest/docs/framework/react/start/getting-started and everything worked perfectly. After testing several features of TanStack, I tried using validateSearch. However, I noticed that once I use validateSearch in createFileRoute, none of the onClick buttons work anymore. Is this normal behavior?
Additionally, I was wondering how to determine if a component is rendered server-side (SSR) or client-side (CSR). For example, with Astro, you need to explicitly specify client:load on a component to ensure it runs as CSR and its JavaScript functionalities work properly. Thanks in advance for your help! ๐Ÿ™
8 Replies
other-emerald
other-emeraldโ€ข10mo ago
Hello, for your first issue (onClick not working) it seems like something crashed during hydration and it didn't complete. Can you check for console errors in the Browser DevTools? That can give us some idea on what could be breaking ๐Ÿ™‚ Regarding your second question, everything should be server rendered on the first load, and then client rendered for subsequents navigations. Remember when doing server rendering, everything inside useEffect won't execute on the server and it will run on the client after hydration
blank-aquamarine
blank-aquamarineOPโ€ข10mo ago
Here is my console when I activate validateSearch:
No description
blank-aquamarine
blank-aquamarineOPโ€ข10mo ago
And here it is when I remove validateSearch:
No description
blank-aquamarine
blank-aquamarineOPโ€ข10mo ago
๐Ÿ™
absent-sapphire
absent-sapphireโ€ข10mo ago
please provide a minimal complete example
blank-aquamarine
blank-aquamarineOPโ€ข10mo ago
Here is the file I am having an issue with: https://github.com/GardoTL/test-tanstack/blob/main/app/routes/index.tsx ๐Ÿ™
GitHub
test-tanstack/app/routes/index.tsx at main ยท GardoTL/test-tanstack
Contribute to GardoTL/test-tanstack development by creating an account on GitHub.
absent-sapphire
absent-sapphireโ€ข10mo ago
can you please strip it down to the bare minimum?
blank-aquamarine
blank-aquamarineOPโ€ข10mo ago
Yes
import { createFileRoute, useRouter } from "@tanstack/react-router";
import { z } from "vinxi";
import { zodValidator } from "@tanstack/zod-adapter";
import { createServerFn } from "@tanstack/start";
import * as fs from "node:fs";

const productSearchSchema = z.object({
page: z.preprocess(
(val) => (typeof val === "string" ? parseInt(val, 10) : val),
z.number().default(1).optional()
),
filter: z.string().default("").optional(),
sort: z.enum(["newest", "oldest", "price"]).default("newest").optional(),
});

const filePath = "count.txt";

const readCount = async () => {
return parseInt(
await fs.promises.readFile(filePath, "utf-8").catch(() => "0")
);
};

const getCount = createServerFn({
method: "GET",
}).handler(() => {
return readCount();
});

const updateCount = createServerFn({ method: "POST" })
.validator((d: number) => d)
.handler(async ({ data }) => {
const count = await readCount();

await fs.promises.writeFile(filePath, `${count + data}`);
});

const Home = () => {
const router = useRouter();
const state = Route.useLoaderData();

return (
<div className="flex flex-col gap-4 p-6">
<button
type="button"
onClick={() => {
updateCount({ data: 1 }).then(() => {
router.invalidate();
});
}}
>
Add 1 to {state}?
</button>
</div>
);
};

export const Route = createFileRoute("/")({
component: Home,
// validateSearch: zodValidator(productSearchSchema),
loader: async () => await getCount(),
});
import { createFileRoute, useRouter } from "@tanstack/react-router";
import { z } from "vinxi";
import { zodValidator } from "@tanstack/zod-adapter";
import { createServerFn } from "@tanstack/start";
import * as fs from "node:fs";

const productSearchSchema = z.object({
page: z.preprocess(
(val) => (typeof val === "string" ? parseInt(val, 10) : val),
z.number().default(1).optional()
),
filter: z.string().default("").optional(),
sort: z.enum(["newest", "oldest", "price"]).default("newest").optional(),
});

const filePath = "count.txt";

const readCount = async () => {
return parseInt(
await fs.promises.readFile(filePath, "utf-8").catch(() => "0")
);
};

const getCount = createServerFn({
method: "GET",
}).handler(() => {
return readCount();
});

const updateCount = createServerFn({ method: "POST" })
.validator((d: number) => d)
.handler(async ({ data }) => {
const count = await readCount();

await fs.promises.writeFile(filePath, `${count + data}`);
});

const Home = () => {
const router = useRouter();
const state = Route.useLoaderData();

return (
<div className="flex flex-col gap-4 p-6">
<button
type="button"
onClick={() => {
updateCount({ data: 1 }).then(() => {
router.invalidate();
});
}}
>
Add 1 to {state}?
</button>
</div>
);
};

export const Route = createFileRoute("/")({
component: Home,
// validateSearch: zodValidator(productSearchSchema),
loader: async () => await getCount(),
});

Did you find this page helpful?