page-component-constraints
Håndhev at sidekomponenter er tynne layout-innpakninger — små i størrelse og fri for datahentingslogikk.
Regeldetaljer
Section titled “Regeldetaljer”I frontend-arkitekturer som skiller ruting fra logikk, bør sidekomponenter bare sette sammen layout og delegere datahenting til Connected-komponenter. Denne regelen håndhever to begrensninger i en enkelt regelfil:
- Størrelsesgrense — Sidekomponenter må holde seg under et konfigurerbart linjetall (standard: 75 linjer).
- Ingen data-hooks — Sidekomponenter må ikke bruke
useState,useQuery,useMutationeller andre datahentings-hooks direkte.
Eksempler på feil kode
Section titled “Eksempler på feil kode”import { useQuery } from "@tanstack/react-query";import { useState } from "react";
export default function DashboardPage() { const [filter, setFilter] = useState(""); // ✗ state hook const { data } = useQuery({ queryKey: ["dashboard"] }); // ✗ data hook // ... 120 lines of layout + logic}Eksempler på riktig kode
Section titled “Eksempler på riktig kode”import { DashboardConnected } from "../components/DashboardConnected";import { Sidebar } from "../components/Sidebar";
export default function DashboardPage() { return ( <div className="flex"> <Sidebar /> <DashboardConnected /> </div> );}Regelimplementasjon
Section titled “Regelimplementasjon”/// <reference path="../rules.d.ts" />
const PAGE_MAX_LINES = 75;
export default { rules: { "page-max-lines": { description: `Page components must be under ${PAGE_MAX_LINES} lines`, async check(ctx) { const pageFiles = await ctx.glob("src/pages/*Page.tsx");
for (const file of pageFiles) { if (file.includes(".test.")) continue;
const content = await ctx.readFile(file); const lineCount = content.split("\n").length;
if (lineCount > PAGE_MAX_LINES) { ctx.report.violation({ message: `Page component has ${lineCount} lines (max ${PAGE_MAX_LINES}). Extract logic to Connected components.`, file, fix: "Move data-fetching and business logic to Connected components", }); } } }, }, "page-no-data-hooks": { description: "Page components must not use data-fetching or state hooks", async check(ctx) { const pageFiles = await ctx.glob("src/pages/*Page.tsx");
const FORBIDDEN_HOOKS = /\b(useState|useForm|useQuery|useMutation|useSuspenseQuery|useInfiniteQuery)\s*[<(]/g;
const ALLOWED_HOOKS = new Set([ "useParams", "useNavigate", "useRouter", "useMatch", "useLocation", "useSearch", ]);
for (const file of pageFiles) { if (file.includes(".test.")) continue;
const content = await ctx.readFile(file);
let match; while ((match = FORBIDDEN_HOOKS.exec(content)) !== null) { ctx.report.violation({ message: `Page component uses "${match[1]}" hook. Extract to a Connected component.`, file, fix: `Move the "${match[1]}" hook to a Connected component`, }); } } }, }, },} satisfies RuleSet;Når du bør bruke den
Section titled “Når du bør bruke den”Når frontend-arkitekturen din følger page/container/presentational-mønsteret og du vil håndheve at sider forblir tynne rutingsendepunkter. Juster PAGE_MAX_LINES og hook-listene for ditt rammeverk.
Når du ikke bør bruke den
Section titled “Når du ikke bør bruke den”Når sider forventes å inneholde logikk (f.eks. i Next.js server-komponenter der datahenting i siden er idiomatisk), eller når prosjektet ditt ikke følger dette arkitekturmønsteret.