In today's episode, I would like to talk about application routing. We will look into some basics of working with Router in Angular and React and consider how those routers are different.
Angular being a "batteries included" framework is supplied with a Router module already. Therefore starting to use a router is a matter of importing that module. However, for React development, there are a couple of different routing libraries available. In this post, we will use react-router which is, probably, the most well-known among them.
Static vs Dynamic Routing
Before we dive into comparing routing implementation in the apps, it's essential to understand the fundamental difference between the two routers.
Angular Router provides static routing. That means that all routes are declared during the app initialisation. Depending on the app size routing is usually defined either for the entire project in the app-routing.module.ts
or for every view module individually.
React Router (since v4), on the other side, employs a concept of dynamic routing. Routes are declared during the application render. Every router piece is a React component which makes it very modular and flexible. Dynamic routing allows changing route structure on the fly, for example, it can change based on some conditional logic.
Simple routing
Now let's look at the Angular and React routing in action. Let's imagine that we are building an application for an e-commerce store. It will have a home page, product catalogue and a page for every product with a short description.
In Angular we create our HomePageComponent
and ProductsPageComponent
as well as provide routing definition in app-routing.module.ts
.
// Angular
// app.component.ts
@Component({
selector: 'app-home-page',
template: `<div>I am a home page</div>`,
})
export class HomePageComponent {}
@Component({
selector: 'app-product-page',
template: `
<ul>
<li *ngFor="let product of products">{{ product.value }}</li>
</ul>
`,
})
export class ProductsPageComponent {
public products = [
{ id: 1, value: 'candies' },
{ id: 2, value: 'ice cream' },
];
}
@Component({
selector: 'app-root',
template: `
<nav>
<ul>
<li><a routerLink="/">Home</a></li>
<li><a routerLink="/products">Products</a></li>
</ul>
</nav>
<router-outlet></router-outlet>
`,
})
export class AppComponent {}
// Angular
// app-routing.module.ts
const routes: Routes = [
{
path: '',
component: HomePageComponent,
},
{
path: 'products',
component: ProductsPageComponent,
},
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
})
export class AppRoutingModule {}
// Angular
// app.module.ts
@NgModule({
declarations: [AppComponent, HomePageComponent, ProductsPageComponent],
// Now we can use our routing module in the app.
imports: [CommonModule, BrowserModule, AppRoutingModule],
providers: [],
bootstrap: [AppComponent],
})
export class AppModule {}
Here is how Angular Router works:
- Router listens to the changes in the URL
- If there is a match with one of the defined paths, Router will execute route-guards and resolvers
- Then a component specified in the
component
field will be rendered in therouter-outlet
router-outlet
is a special Angular component, which, similarly to ng-content
, is used as a placeholder for the content matching current URL.
// React
const HomePage = () => {
return <div>I am a home page</div>
}
const ProductsPage = () => {
const products = [{id: 1, value: "candies"}, {id: 2, value: "ice cream"}]
return (
<ul>
{ products.map((product) => <li key={product.id}>{product.value}</li> ) }
</ul>
)
}
function App() {
return (
<BrowserRouter>
<nav>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/products">Products</Link></li>
</ul>
</nav>
<Switch>
<Route path="/products"><ProductsPage /></Route>
<Route path="/"><HomePage /></Route>
</Switch>
</BrowserRouter>
);
}
export default App;
To enable routing in React, we need to wrap our application with BrowserRouter
and define routes in the Switch
. Anything added inside the Route
component, will be rendered if route path matches the URL.
Redirects
Now let's assume we want our home page to be available on the /home
path instead of the root. Let's update the routing and make sure, if the user visits the root she is redirected to the proper home page.
// Angular
// app-routing.module.ts
const routes: Routes = [
{
path: '',
redirectTo: 'home',
pathMatch: 'full',
},
{
path: 'products',
component: ProductsPageComponent,
},
{
path: 'home',
component: HomePageComponent,
},
];
// React
function App() {
return (
<BrowserRouter>
<nav>
<ul>
<li><Link to="/home">Home</Link></li>
<li><Link to="/products">Products</Link></li>
</ul>
</nav>
<Switch>
<Route path="/products"><ProductsPage /></Route>
<Route path="/home"><HomePage /></Route>
<Redirect from="/" to="/home" />
</Switch>
</BrowserRouter>
);
}
Unlike an additional route definition in Angular, In React, Redirect
is a component from the react-router library, which will handle redirects for your app.
Route parameters
Application routing is especially helpful when we want to share some state across the application via the URL parameters. To enable dynamic parameters we only have to add a new route listener with a parameter name. Both Angular and React use :paramName
syntax to map value to the parameter name.
// Angular
// app-routing.module.ts
const routes: Routes = [
...
{
path: 'products',
children: [
{ path: '', component: ProductsPageComponent },
{ path: ':productId', component: SingleProductPageComponent },
],
},
...
];
// Angular
// app.component.ts
...
@Component({
selector: 'app-product-page',
template: `
<ul>
<li *ngFor="let product of products" [routerLink]="product.id">
<!-- routerLink is a directive which helps navigating application router -->
<a [routerLink]="product.id">{{ product.value }}</a>
</li>
</ul>
`,
})
export class ProductsPageComponent {
public products = [
{ id: 1, value: 'candies' },
{ id: 2, value: 'ice cream' },
];
}
@Component({
selector: 'app-single-product-page',
template: ` <div>{{ product$ | async | json }}</div> `,
})
export class SingleProductPageComponent {
product$ = this.activatedRoute.paramMap.pipe(
map((params) => params.get('productId')),
map((id) => this.products[id])
);
// Data about the product might be coming from the API or from the application state.
private products = {
1: {
name: 'candies',
description: 'candies are sweet',
},
2: {
name: 'ice cream',
description: 'ice cream is cold',
},
};
constructor(private activatedRoute: ActivatedRoute) {}
}
// React
...
const ProductsPage = () => {
const products = [{id: 1, value: "candies"}, {id: 2, value: "ice cream"}]
return (
<ul>
{ products.map((product) => (
<li key={product.id}>
{/* We can use Link component to navigate application router */}
<Link to={`/products/${product.id}`}>{product.value}</Link>
</li>
)) }
</ul>
)
}
const SingleProductPage = () => {
// useParams hooks help accessing router context and retrieving parameter values
const { productId } = useParams()
// Data about the product might be coming from the API or from the application state.
const products = {
1: {
name: 'candies',
description: 'candies are sweet',
},
2: {
name: 'ice cream',
description: 'ice cream is cold',
},
};
return <div>{JSON.stringify(products[productId])}</div>
}
function App() {
return (
<BrowserRouter>
<nav>
<ul>
<li><Link to="/home">Home</Link></li>
<li><Link to="/products">Products</Link></li>
</ul>
</nav>
<Switch>
<Route path="/products/:productId"><SingleProductPage /></Route>
<Route path="/products"><ProductsPage /></Route>
<Route path="/home"><HomePage /></Route>
<Redirect from="/" to="/home" />
</Switch>
</BrowserRouter>
);
}
As you can see, using route parameters is very similar in Angular and React. To dynamically navigate inside the app, we are using routerLink
directive in Angular and Link
component in React. The only significant difference is accessing route parameters from the component.
In Angular we have to inject ActivatedRoute
, a special class that keeps the information about the current route and its available parameters. React Router on the other side uses Context API which can be accessed by hooks (like in the example above).
Summary
Although router implementations for Angular and React are different, using them in the application is very similar. To make it work we need just three pieces of code:
- Routes definition
- Components that are rendered when the route is activated
- Access to the ActivatedRoute or RouterContext if you want to retrieve parameters
Thanks for reading! If you like my posts help to spread the word and follow me on Twitter 🚀
Top comments (0)