-
Notifications
You must be signed in to change notification settings - Fork 0
React Routes
In React we will likely be building an SPA, or Single Page Application. This means we won't require multiple pages to be loaded from the server, just the original GET request with our initial HTML, CSS and JS files. This requires us to figure out how to make the experience of Client-Side routing work to our advantage.
When we make server calls we are making a GET request to a URL and that new URL is in our address bar. If we have visited a few different URL's that information is saved in browser history.
Go to the JavaScript console in Chrome and type
window.historyThis should return the following code.
History { length: 32, state: null, scrollRestoration: "auto" };The length is how many locations you have visited in this window session.
Now if you type the following code it will take you to the last location in your browser history.
window.history.back();
window.history.forward();JavaScript's History API also gives the ability to pushState(state_obj, title, url) to the history entries.
It takes three arguments:
-
state object: This is a plain JavaScript object that is associated with the new history entry we are going to create with the pushState() function.
-
title: This is currently ignored by most browsers and it is safe to just pass an empty string or a title here.
-
url: This is the URL for the new history entry. The browser will not attempt to load this URL after it calls pushState().
const newState = {
goal: "Learn about pushState()"
};
window.history.pushState(newState, "new state", "new-state");
// You should notice that your browser has now changed to show new-state at the end of your URL address.
> window.history.state
//=> Object { goal: "Learn about pushState()" }If you now use the window.history.back() function you will not go back to the previous page, but your URL address will return to the original URL address. If you use window.history.forward() you will move back to our new URL that ends in new-state
import React from 'react';
import {
BrowserRouter as Router,
Route
} from 'react-router-dom';
import NavBar from '../components/NavBar';
import Home from '../components/Home';
import Actors from '../components/Actors';
import Directors from '../components/Directors';
import Movies from '../components/Movies';
const App = (props) => {
return (
<Router>
<div className="app">
<NavBar />
<Route exact path="/" component={Home} />
<Route exact path="/actors" component={Actors} />
<Route exact path="/directors" component={Directors} />
<Route exact path="/movies" component={Movies} />
</div>
</Router>
);
};
export default AppInstead of listing two Routes side by side, with React-Router, we can make the master-detail pattern by making our Item component the child of the List component.
Think of YouTube again for a moment. Let's pretend that visiting /videos displays a list of videos. Clicking on any video should keep our list of videos on the page, but also display details on the selected video. This should be updated in the URL - the URL should change to /videos/:videoId, where :videoId is a unique value (this is slightly different than how YouTube works but the concepts are similar). Using nested React-Router, we can write our application so one component, the List (of videos) renders using a Route that matches the path /videos. Then, within List, we add a second Route that renders Item when the path matches /videos/:videoId.
Router wrapping everything inside the JSX code. All JSX wrapped within Router can use Routes, including the JSX from any child components.
class App extends Component {
state = {
movies: {
1: { id: 1, title: 'A River Runs Through It' },
2: { id: 2, title: 'Se7en' },
3: { id: 3, title: 'Inception' }
}
}
render() {
return (
<Router>
<div>
<NavBar />
<Route exact path="/" render={() => <div>Home</div>} />
<Route path='/movies' render={routerProps => <MoviesPage {...routerProps} movies={this.state.movies}/>} />
</div>
</Router>
);
}
}notice that instead of <Route exact path="/" component= ... we use render={ ....
Instead of passing an entire component in, we must pass a function that returns JSX.
The render prop function accepts an argument, routerProps.
When the path matches the URL, the Route will call the function inside render and pass in the current information available about the route, including the URL path that caused the Route to render.
MoviesPage, receives props from the Route that contain information on the route. In addition, we can also pass in other props, as we see with movies={this.state.movies}.
// ./src/containers/MoviesPage.js
// this is just presentation Component
import React from 'react';
import { Route } from 'react-router-dom';
import MoviesList from '../components/MoviesList';
import MovieShow from '../components/MovieShow';
const MoviesPage = ({ match, movies }) => (
<div>
<MoviesList movies={movies} /
<Route exact path={match.url} render={() => <h3>Choose a movie from the list above</h3>}/>
// <Route path={`${match.url}/:movieId`} component={<MovieShow movies={movies} /> }/> :we cannot pass information about the route here
<Route path={`${match.url}/:movieId`} render={routerProps => <MovieShow {...routerProps} movies={movies} /> } />
</div>
)
export default MoviesPage
// ./src/components/MoviesList.js
import React from 'react';
import { Link } from 'react-router-dom';
const MoviesList = ({ movies }) => {
const renderMovies = Object.keys(movies).map(movieID =>
<Link key={movieID} to={`/movies/${movieID}`}>{movies[movieID].title}</Link>
);
return (
<div>
{renderMovies}
</div>
);
};
export default MoviesList;
// ./src/components/MovieShow.js
import React from 'react';
const MovieShow = ({match, movies}) => {
return (
<div>
<h3>{ movies[match.params.movieId].title }</h3>
</div>
);
}
export default MovieShow;You will notice that we are now inheriting match from this.props in ./src/containers/MoviesPage.js. This comes from the routerProps passed in from App. This is a POJO (plain old Javascript object) that contains the current URL.
:movieId represents a parameter. If we visit http://localhost:3000/movies/1, the movieId parameter would be "1".
One of the props we receive from the Route is match, and match contains all the parameters from the URL. These parameters are stored as key/value pairs in match.params. The key corresponds to whatever we named the parameter in our Route, so in this case, the parameter will be movieId.
To get nested Routes to work, we need to utilize route information that is stored in the match props. match contains both the current URL path in match.url, as well as any parameters in match.params. We define the parameter names in a Route's path by prepending a colon (:) to the front of the name. This name will then show up as a key inside match.params.
- Mounting -
static getDerivedStateFromProps(props, state)- Mounting -
componentDidMount()- Updating -
static getDerivedStateFromProps(props, state)- Updating -
shouldComponentUpdate(nextProps, nextState)- Updating -
render()- Updating -
getSnapshotBeforeUpdate(prevProps, prevState)- Updating -
componentDidUpdate(prevProps, prevState, snapshot)- Unmounting -
componentWillUnmount()- Element Ref