In this post, I would like to introduce my approach to "Named URLs" for React Router.
Motivation
I used to use Django's URLs reverse
function. This function is beneficial when you need to get an actual URL using a named URL pattern or callable view object.
I want to use a similar approach with React Router.
Problems
The problems I am trying to solve are:
-
Avoid hard-coded URLs. In the most examples I found, URLs hard-coded in
Link
. I feel like this can lead us to broken links during refactoring, etc. - Missing URL params. It is difficult to identify Hard-coded links with missed params until you click on them.
Solution
The project directories are structured this way.
The Reverse
implementation.
// src/shared/utils/namedUrls.js
import { compile } from 'path-to-regexp';
export class RoutePath {
reverse(params) {
const reversed = compile(this.pattern);
return reversed(params);
}
constructor(pattern, component) {
if (!pattern || !component) throw new Error('`pattern` and `component` are required');
this.pattern = pattern;
this.component = component;
}
}
This class helps us build a route that knows how to build a link using given parameters and patterns.
The usage.
In the app directory.
// src/apps/account/routes.js
import SignInPage from 'apps/account/pages/SignIn';
import UserPage from 'apps/account/pages/User';
import { RoutePath } from 'shared/utils/namedUrls';
const basePath = '/account/';
export default {
signIn: new RoutePath(`${basePath}sign-in/`, SignInPage),
user: new RoutePath(`${basePath}user/:id/`, UserPage),
};
In the main routes.js
.
// src/routes.js
import { prepareRoutes } from 'shared/utils/namedUrls';
import accountRoutes from './apps/account/routes';
export const routes = {
accountRoutes,
};
export const preparedRoutes = prepareRoutes(routes);
In components
import React from 'react';
import { Link } from 'react-router-dom';
import { routes } from 'routes';
const SomeComponent = () => (
<>
<Link to={routes.accountRoutes.signIn.reverse()}>Sign In</Link>
<Link to={routes.accountRoutes.user.reverse({ id: 1 })}>User</Link>
</>
);
export default SomeComponent;
Rendered links are:
<a href="/account/sign-in/">Sign In</a>
<a href="/account/user/1/">User</a>
Error handling
I didn't implement any additional error handling as I am satisfied with the path-to-regexp
app's error handling.
If you miss some URL pattern parameters, you will find errors like the below in the browser console.
index.ts:337 Uncaught TypeError: Expected "id" to be a string
at index.ts:337
at RoutePath.reverse (namedUrls.js:12)
at App (App.js:62)
I suppose this will be enough to warn developers that they missed the URL pattern's parameters. And I feel like these errors should be helpful during e2e testing.
Feedback is welcome!
Top comments (0)