Ext JS to React: Routing

   JavaScript
Ext JS to React: How to Configure Routes

This is part of the Ext JS to React blog series. You can review the code from this article on the Ext JS to React Git repo.

Ext JS introduced routes as an enhancement over the history class used to coordinate browser history and an application UI. What routes offers is the ability to intimately map the views in the UI to the browser’s address bar. React applications may also take advantage of routes to render the correct UI based on the current URL. In this article we’ll look at the leading router solution in the React ecosystem, react-router. We’ll explore how you can define your application to predictably render all aspects of your application using the browser URL, including user navigation.

Note: Full disclosure, Modus Create is a sponsor of react-router so, yeah, we like it. 😃

Note: While not a requirement for React, this article’s code examples assume you’re starting with a starter app generated by create-react-app.

React Router Overview

Install react-router:

npm install --save react-router-dom

The react-router module handles interacting with the browser’s address bar and history manager. In Ext JS the router handled only the portion of the url following the hash (or optionally #! in later versions). Routes with react-router may be handled using either the content within the hash or within the URL itself. You can set up routing using the HashRouter component to handle routes like:

appRoot#view/subview/itemId

However, react-router recommends using the BrowserRouter component instead to handle routes defined in the url itself ahead of any hash:

appRoot/view/subview/itemId

The BrowserRouter component uses the HTML5 history API and will not be compatible with all browsers. Additionally, you’ll need to ensure that your web server is configured to allow real url navigation versus folder navigation.

react-router Simple Route

Let’s set up a simple route example to demonstrate how react-router works. In this first example, we’ll set up our App with two views that will be rendered depending on the URL. Let’s create the following files:

src/Home.js

import React from 'react';
export default () => <h2>Home</h2>;

src/User.js

import React from 'react';
export default () => <h2>User</h2>;

src/App.js

import React from 'react';
import { BrowserRouter as Router, Route } from 'react-router-dom';
import Home from './Home';
import User from './User';

const App = () => (
  <Router>
    <div>
      <Route exact path="/" component={Home} />
      <Route path="/user" component={User} />
    </div>
  </Router>
);

export default App;


Ext JS to React: How to Configure Routes, User



Our App class serves as a container for either Home or User depending on the URL used. If we run npm start in the terminal, we’ll see “Home” in the browser since by default the URL loaded is the root URL of http://localhost:3000/. The Home component is shown by navigating to the application root by stipulating the Route as:

<Route exact path="/" component={Home}/>

The path value of “/” tells react-router that when we’re at the root with no additional pathing in the URL the Home component is to be rendered. If we want to render the User view instead, we can navigate to http://localhost:3000/user. Since our user route is defined with a path of “/user” the User component, not the Home component, will be rendered.

You can review the sample code on the Git repo.

react-router Nested Routes and Params

Now that we have a simple routing example laid out, let’s add nested routes to it. Most applications won’t be a single layer deep. Our User class, for example, could be a container displaying a list of users by default. Additionally, if a user’s id is appended to the URL, then that user’s info would display in an edit form. Think of clicking a row in a master grid to view record details. Let’s modify the User class and add a UserList and UserForm class as child components of User.

src/User.js

import React from 'react';
import { BrowserRouter as Router, Route } from 'react-router-dom';
import UserForm from './UserForm';
import UserList from './UserList';

export default ({ match }) => (
  <div>
    <UserList />
    <Router>
      <Route path={`${match.url}/:userId`} component={UserForm}/>
    </Router>
  </div>
);

src/UserList.js

import React from 'react';
export default () => <h2>User List</h2>;

src/UserForm.js

import React from 'react';

export default (props) => {
  const { userId } = props.match.params;

  return <h2>User Id: {userId}</h2>;
};


Ext JS to React: How to Configure Routes, Nested



Our User class now displays the UserList as a child and optionally a UserForm component if the URL has a value after “user/” in the URL. For this example, we could assume that that would be a user’s ID and the URL might look like:

http://localhost:3000/user/1234

If the URL were only http://localhost:3000/user then the User view and the UserList would be rendered. The path attribute of our User class’s Route is {`${match.url}/:userId`}. The use of “:” in the path means that any value supplied in the URL will match and be will automatically passed down to the component specified on the Route. Its value will be stored as a param on the match prop passed to the Route’s component, UserForm in the case of our example. Here we render the value in the UserForm in plain text.

const { userId } = props.match.params;
…
<h2>User Id: {userId}</h2>

However, this user ID value could easily be used to asynchronously fetch user data to populate the UserForm once mounted.

You can review the sample code on the Git repo.

react-router Route Navigation

Now that we’ve looked at creating the route structure, let’s look at how to populate the UI to activate a route either programmatically or via user interaction.

Programmatic Navigation

First, let’s look at how the route can be activated in the logic of a class. The react-router module wraps its internal history api and can be used to set the URL or url segment programmatically. As an arbitrary example to demonstrate how routing can be navigated let’s make an arbitrary change to our example UserForm class:

const UserForm = (props) => {
  const { userId } = props.match.params;
  const { history } = props;

  if (userId === '1234') {
    history.push('abcd');
  }

  return (
    <div>
      <h2>User Id: {userId}</h2>
    </div>
  )
}

A component rendered via a Route will have history passed as a prop from the Route. We can use history’s push method to swap the current matched portion of the route with another string. The conditional in the example above does just that. Setting the URL to http://localhost:3000/user/1234 results in the URL changing to http://localhost:3000/user/abcd as the UserForm is rendered. The push method adds the URL to browser history, which in this case may not be the desired action. The replace method will instead swap the current URL in the browser’s history with the one passed in.

You can review the sample code on the Git repo.

Simple Navigation

Link components can be used for user navigation as you would use an anchor tag. Let’s modify our example app to have three navigation links at the top of the view. We’ll also add an About class as a peer of User and Home. The links allow a user to select the home, user, or about view to display below the links.

src/About.js

const About = () => (
  <div>
    <h2>About</h2>
  </div>
)

src/App.js

import React from 'react';
import { BrowserRouter as Router, Link, Route } from 'react-router-dom';
import About from './About';
import Home from './Home';
import User from './User';

const App = () => (
  <div>
    <Router>
      <div>
        <Link to="/">Home<br/></Link>
        <Link to="/user">User<br/></Link>
        <Link to="/about">About</Link>

        <Route exact path="/" component={Home}/>
        <Route path="/user" component={User}/>
        <Route path="/about" component={About}/>
      </div>
    </Router>
  </div>
);

export default App;


Ext JS to React: How to Configure Routes, Simple Links



Here we’ve added three Link components to the parent Router with to attributes that mirror the paths defined on each Route. Clicking on the text from each Link will update the URL which, in turn, updates the view.

You can review the sample code on the Git repo.

Tab Navigation

Lastly, let’s modify our previous Link example to instead use the NavLink component. The NavLink is an enhanced Link component that adds a className and / or style prop to the NavLink instance when the current URL matches the NavLink’s to prop. The NavLink component makes it easy style the navigation elements based on the current URL resulting in UI’s like we’re used to with Ext JS’s Tab Panel.

src/App.js

import React from 'react';
import { BrowserRouter as Router, NavLink, Route } from 'react-router-dom';
import About from './About';
import Home from './Home';
import User from './User';
import './App.css';

const App = () => (
  <div>
    <Router>
      <div className="nav-links">
        <NavLink exact activeClassName="active" to="/">Home</NavLink>
        <NavLink exact activeClassName="active" to="/user">User</NavLink>
        <NavLink exact activeClassName="active" to="/about">About</NavLink>

        <Route exact path="/" component={Home}/>
        <Route path="/user" component={User}/>
        <Route path="/about" component={About}/>
      </div>
    </Router>
  </div>
);

export default App;

In this example, we add a className of “nav-links” to the div containing the NavLinks so that we can decorate the navigation elements like tabs. The activeClassName prop on each NavLink serves to add “active” as a class name when the Route path and NavLink to props match which shows an “active tab” decoration. For completeness, here is the CSS you might use for tab styling:

src/App.css

.nav-links a {
  display: inline-block;
  border-bottom: 4px solid transparent;
  padding: 6px 12px;
  text-decoration: none;
  color: #555;
}
.nav-links a.active {
  border-bottom-color: #6597ca;
}


Ext JS to React: How to Configure Routes, Tabbed Links



You can review the sample code on the Git repo.

Conclusion

Routing enables your application to operate in the browser’s address bar similar to how you might expect a website to behave. Each view within your site relates to the browser URL which allows your users to not link to not only the application front page, but also any route-enabled view within the application. In addition to user convenience, routing offers a centralized navigation pattern for your application to follow where navigable views are rendered by the router’s built-in conditional logic. As we’ve said, we’re big fans of react-router. However, it’s not the only router package available. Have you found a router module you’ve fallen in love with? Please share your experiences in the comments below!


Like What You See?

Got any questions?