S
SolidJS•7d ago
MaveriX89

How to Abstract Routers using Classes

I'm trying to create a library that leverages a subset of routing functions under the hood. With the release of TanStack Router for SolidJS, I do not want to couple the library specifically to Solid Router and want to provide a Router abstraction that consumers of the library can configure with their router of choice. The way I'm going about that is providing abstract Router classes. Below is one example:
import { type Accessor, createSignal } from "solid-js";

import type { Router } from "./types";

export class SolidRouter implements Router {
private router: typeof import("@solidjs/router") | undefined;
public loaded: Accessor<boolean>;

constructor() {
const [loaded, setLoaded] = createSignal(false);

this.loaded = loaded;

import("@solidjs/router").then((routerModule) => {
this.router = routerModule;
setLoaded(true);
});
}

pathname = () => {
if (!this.router || !this.loaded()) {
throw new Error("Router not loaded");
}

return this.router.useLocation().pathname;
};

getSearchParam = (key: string) => {
if (!this.router || !this.loaded()) {
throw new Error("Router not loaded");
}

const [searchParams] = this.router.useSearchParams();
return searchParams[key] as string | null;
};

navigate = (path: string, options?: { replace?: boolean }) => {
if (!this.router || !this.loaded()) {
throw new Error("Router not loaded");
}

const _navigate = this.router.useNavigate();
_navigate(path, options);
};
}
import { type Accessor, createSignal } from "solid-js";

import type { Router } from "./types";

export class SolidRouter implements Router {
private router: typeof import("@solidjs/router") | undefined;
public loaded: Accessor<boolean>;

constructor() {
const [loaded, setLoaded] = createSignal(false);

this.loaded = loaded;

import("@solidjs/router").then((routerModule) => {
this.router = routerModule;
setLoaded(true);
});
}

pathname = () => {
if (!this.router || !this.loaded()) {
throw new Error("Router not loaded");
}

return this.router.useLocation().pathname;
};

getSearchParam = (key: string) => {
if (!this.router || !this.loaded()) {
throw new Error("Router not loaded");
}

const [searchParams] = this.router.useSearchParams();
return searchParams[key] as string | null;
};

navigate = (path: string, options?: { replace?: boolean }) => {
if (!this.router || !this.loaded()) {
throw new Error("Router not loaded");
}

const _navigate = this.router.useNavigate();
_navigate(path, options);
};
}
The same pattern above is applied for TanStackRouter as I dynamically load the library in the constructor and implement the same interfaces with all the things that TanStack Router expects. I have two primary questions: 1. Is the above paradigm a good way of going about providing the abstraction over routing? If not, what's another way that allows users to configure routing. 2. Every time I use the class, I am getting an exception at the point of the navigate interface being called despite it being invoked while being a descendant of a <Router /> component in the JSX tree:
Uncaught (in promise) Error: <A> and 'use' router primitives can be only used inside a Route.
at invariant (utils.js?v=23f17e0f:29:15)
at useRouter (routing.js?v=23f17e0f:9:32)
at Module.useNavigate (routing.js?v=23f17e0f:42:34)
at SolidRouter.navigate (SolidRouter.tsx:42:35)
at runCallback (useLib.ts:120:14)
Uncaught (in promise) Error: <A> and 'use' router primitives can be only used inside a Route.
at invariant (utils.js?v=23f17e0f:29:15)
at useRouter (routing.js?v=23f17e0f:9:32)
at Module.useNavigate (routing.js?v=23f17e0f:42:34)
at SolidRouter.navigate (SolidRouter.tsx:42:35)
at runCallback (useLib.ts:120:14)
Not sure how to debug the error. I suppose it has something to do with invoking const _navigate = this.router.useNavigate(); in the context of a class and not a component? 🤔
0 Replies
No replies yetBe the first to reply to this messageJoin

Did you find this page helpful?