Routing
Overview
- Similar in function to a server-side router in an MVC framework
- Associates a route (url) with a particular controller action
- React Router switches between (page/container) components when a route changes
- Back button is broken by default when page/container components change
- the browser's history is not updated with a new url when page/container components change
- React Router programmatically adds entries to the browser's history
- enables the back button to work in React applications
There are two versions:
- BrowserRouter (react-router-dom) for web applications.
- NativeRouter (react-router-native) for use with React Native.
Installation
Install the package
npm install react-router-dom@6Add the script tag
index.html...<script src="/node_modules/react/umd/react.development.js"></script><script src="/node_modules/react-dom/umd/react-dom.development.js"></script>+ <script src="/node_modules/history/umd/history.development.js"></script>+ <script src="/node_modules/react-router/umd/react-router.development.js"></script>+ <script src="/node_modules/react-router-dom/umd/react-router-dom.development.js"></script><script src="/node_modules/@babel/standalone/babel.min.js"></script><script src="/node_modules/axios/dist/axios.min.js"></script><script type="text/babel" src="/main.js"></script>! Be sure that the
main.jsscript tag's src attribute starts with a/or the router will not work properly when you refresh the page.Log the
RouterRouterDOMto verify it is installed properlymain.jsconsole.log(window.ReactRouterDOM);In the console you should see:
{BrowserRouter: ƒ, HashRouter: ƒ, Link: ƒ, …}
Basics
Add these styles
/* styles.css */.container {border: 1px solid #ddd;margin: 30px;padding: 30px;}nav ul {list-style: none;}nav ul li {display: inline;}nav ul li:after {content: " | ";}nav ul li:last-child:after {content: "";}Try this code
const {BrowserRouter: Router,Route,Routes,Link,NavLink,Navigate,useParams,useLocation,useNavigation,} = window.ReactRouterDOM;function Home() {return <h2>Home</h2>;}function About() {return <h2>About</h2>;}function Contact() {return <h2>Contact</h2>;}function App() {return (<Router><div><nav><ul><li><Link to="/">Home</Link></li><li><Link to="/about">About</Link></li><li><Link to="/contact">Contact</Link></li></ul></nav><div className="container"><Routes><Route path="/" element={<Home />} /><Route path="about" element={<About />} /><Route path="contact" element={<Contact />} /></Routes></div></div></Router>);}ReactDOM.createRoot(document.getElementById('root')).render(<App />);Change the Link tags to NavLink tags
<nav><ul><li><NavLink to="/">Home</NavLink></li><li><NavLink to="/about">About</NavLink></li><li><NavLink to="/contact">Contact</NavLink></li></ul></nav>Add the following style
styles.css... .active {background-color: #bee5eb;padding: 10px;}Refresh the browser and see the navigation items are highlighted.
Not Found (404)
Be sure you are running a development web server like serve with the
-sflag.package.json"scripts": {"start": "serve -s",...},...Change the URL to
http://localhost:5000/norouteThe navigation renders but the page is blank. Ideally, we would like to show a
NotFoundcomponent when this happens.
3) Create a NotFound component
Add a route for it with no path
<Routes><Route path="/" element={<Home />} /><Route path="about" element={<About />} /><Route path="contact" element={<Contact />} />+ <Route path="*" element={<NotFound />} /></Routes>Navigate to the home route and then the contact route and notice the
NotFoundcomponent shows when visiting every routeNavigate to the various routes again and notice that only when you manually go to a route that doesn't exist like:
/noroutetheNotFoundcomponent renders.
URL Parameters
This example builds on the code from the previous demonstrations in this section.
Create a
Moviemodel class.- Add it just before the App component
class Movie {constructor(id, name, description, type) {this.id = id;this.name = name;this.description = description;this.type = type;}}Create an array of mock movies
const movies = [new Movie(1," Titanic","A seventeen-year-old aristocrat falls in love with a kind but poor artist aboard the luxurious, ill-fated R.M.S. Titanic.","Drama"),new Movie(2," E.T. the Extra-Terrestrial","A troubled child summons the courage to help a friendly alien escape Earth and return to his home world.","Fantasy"),new Movie(3,"The Wizard of Oz",// tslint:disable-next-line:max-line-length"Dorothy Gale is swept away from a farm in Kansas to a magical land of Oz in a tornado and embarks on a quest with her new friends to see the Wizard who can help her return home in Kansas and help her friends as well.","Fantasy"),new Movie(4,"Star Wars: Episode IV - A New Hope ",// tslint:disable-next-line:max-line-length"Luke Skywalker joins forces with a Jedi Knight, a cocky pilot, a Wookiee and two droids to save the galaxy from the Empire/`s world-destroying battle-station while also attempting to rescue Princess Leia from the evil Darth Vader.","Action"),];Create a
Moviescomponent to list moviesfunction MovieList({ movies }) {return (<div><h2>Movies</h2><ul>{movies.map((movie) => (<li key={movie.id}><Link to={`./${movie.id}`}>{movie.name}</Link></li>))}</ul></div>);}Add a Route to go to the
MoviescomponentNotice how we pass props to a the
Moviescomponent which is rendered by the React Router<Routes><Route path="/" element={<Home />} /><Route path="about" element={<About />} /><Route path="contact" element={<Contact />} />+ <Route path="movies" element={<MovieList movies={movies} />} /><Route path="movies/:id" element={<MovieDetail />} /><Route path="*" element={<NotFound />} /></Routes>Add a NavLink to navigate to the
Moviescomponent.<nav><ul><li><NavLink to="/">Home</NavLink></li><li><NavLink to="/about">About</NavLink></li><li><NavLink to="/contact">Contact</NavLink></li>+ <li>+ <NavLink to="/movies">Movies</NavLink>+ </li></ul></nav>Create a
MovieDetailcomponent to show the detail about a particular movie.function MovieDetail() {let { id } = useParams();id = Number(id);const movie = movies.find((movie) => movie.id === id);return (<div><h3>{movie.name}</h3><p>{movie.description}</p></div>);}Add a Route to go to the
MovieDetailcomponent....<Routes><Route path="/" element={<Home />} /><Route path="about" element={<About />} /><Route path="contact" element={<Contact />} /><Route path="movies" element={<MovieList movies={movies} />} />+ <Route path="movies/:id" element={<MovieDetail />} /><Route path="*" element={<NotFound />} /></Routes>Test the application and verify you can now click on a movie in the list and the movie detail route and component loads. Verify the browser's back button still works to return to the list after visiting the detail component.
Nesting
Edit the
MovieListcomponent to nest theMovieDetailcomponent route inside itself.function MovieList({ movies }) {return (<div><h2>Movies</h2><ul>{movies.map((movie) => (<li key={movie.id}><Link to={`./${movie.id}`}>{movie.name}</Link></li>))}</ul>+ <div style={{ marginLeft: "40px" }}>+ <Routes>+ <Route path=":id" element={<MovieDetail />} />+ </Routes>+ </div></div>);}Remove the
MovieDetailroute from theAppcomponent. Add an astericks (*) after themoviesroute so it can find the nested routes....<Routes><Route path="/" element={<Home />} /><Route path="about" element={<About />} /><Route path="contact" element={<Contact />} />+ <Route path="movies/*" element={<MovieList movies={movies} />} />- <Route path="movies/:id" element={<MovieDetail />} /><Route path="*" element={<NotFound />} /></Routes>Refresh the browser and notice that the movie detail now shows below the movie list after clicking a movie link.