Handling Routes In React.js | A Custom Approach
Published: 2024-01-19 09:09:11

Hey there! If you've ever worked with React, you know how important routes are—they're what connect different pages or sections of your app. However, dealing with them can be a bit tricky.

So, I came up with a nifty way to handle route paths neatly, all in one file, and easily share them across your entire app.

Let's dive into the code:

```tsx
export const AppRoutePaths = {
  home: '',
  auth: 'auth',
  project: {
    index: 'project',
    create: 'create',
    manage: {
      index: 'manage',
      byId: ({ id }: { id: string }) => `${id}`,
    },
  },
  // ... (other routes)
};

function createAppRouterFullPath(routePaths: Record<string, any>, parentPath = '') {
  let fullPath: Record<string, string> = {};

  for (const key in routePaths) {
    const value = routePaths[key as keyof typeof AppRoutePaths];
    const propKey = parentPath
      ? [parentPath, key]
          .filter((item) => !item.match(/^(?:false|null|undefined|0|NaN|''|""|`.*?`)$/i))
          .join('.')
          .replaceAll('/', '.')
      : key;

    if (typeof value === 'string') {
      fullPath[propKey] = parentPath ? `${parentPath}/${value}` : value;
    } else if (typeof value === 'function') {
      fullPath[propKey] = parentPath
        ? `${parentPath}/${value({ id: ':id' })}`
        : value({ id: ':id' });
    } else if (typeof value === 'object') {
      const nestedFullPath = createAppRouterFullPath(
        value,
        parentPath ? `${parentPath}/${key}` : key
      );
      fullPath = { ...fullPath, ...nestedFullPath };
    }
  }

  return fullPath;
}

export const AppRouterFullPath = createAppRouterFullPath(AppRoutePaths);

```

Now, let's talk about the purpose of the createAppRouterFullPath function:

> If you noticed, the routes inside AppRoutePaths are nested but not connected to their parent. This function helps link them together.

Consider these routes:


```tsx

export const AppRoutePaths = {
  home: '',
  auth: 'auth',
  project: {
    index: 'project',
    byId: ({ id }: { id: string }) => `${id}`, 
  }

```

If I want to link to auth, I can use:

```tsx

<Link href={AppRoutePaths.auth} ... />

```
But what if I'm on the auth route and want to link to byId inside project?

```tsx

<Link href={AppRoutePaths.project.byId({ id: 'id' })} ... />

```

Without the `createAppRouterFullPath` function, this would redirect to `localhost:3000/auth/:id` instead of `localhost:3000/project/:id`. The function takes care of joining nested routes with their parent.

Now, how do you use it? Check this out:

```tsx

const router = useMemo(
  () =>
    createBrowserRouter([
      {
        errorElement: <ErrorBoundary />,
        element: <Layout />,
        children: [
          {
            path: AppRoutePaths.home, // here
            index: true,
            element: (
              <LazyRoute>
                <HomePage />
              </LazyRoute>
            ),
          },
          // ... (other routes)
        ],
      },
    ]),
  []
);

```

I've also thrown in a handy function to add '/' in front of routes when needed, preventing them from becoming relative to the current URL:

```tsx

export function withSlashPrefix(path: string) {
  return `/${path}`;
}

```

Now linking buttons to pages is a breeze:

```tsx

<Link href={withSlashPrefix(AppRoutePaths.home)}>
    <Button>GoToHome</Button>
</Link>

```


It's just one of my explorations; I work with React every day, so I frequently encounter issues and discover reasons to build or update things. This is one of those instances.


Voila! Happy routing!