React Router V4 - Tuong-Nguyen/JavaScript-Structure GitHub Wiki
React Router V4 has been broken three packages: react-router
, react-router-dom
, react-router-native
.
For web development, we should use react-router-dom
.
Using npm:
$ npm install --save react-router-dom
import {
BrowserRouter as Router,
Route,
Link,
Switch,
Redirect,
Prompt,
NavLink} from 'react-router-dom';
const Home = () => <h1>Home</h1>
const About = (props) => {
console.log(props);
return(
<h1>About</h1>
)
}
const Links = () => (
<nav>
<Link to="/">Home</Link>
<Link to={{pathname: '/about'}}>About</Link>
<Link replace to="/contact">Contact</Link>
<Link to="/content">Content</Link>
</nav>
)
const App = () => (
<Router>
<div>
<Links />
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/contact" render={() => <h1>Contact</h1>} />
<Route path="/content" children={() => <h1>Content</h1>} />
</div>
</Router>
)
ReactDOM.render(<App />, document.getElementById('root'));
The example above uses essential components for routing as following:
<Link>
: help to navigate between routes.
<Router>
as <BrowserRouter>
(for browser based projects): keeps track of the current location to keep UI in sync with the URL.
<Route>
: renders some UI when the location's pathname ( a part of URL) matches with the route's path.
There are three ways to render something with a <Route>
:
<Route component>
: a React component.
<Route render>
: allows for inline rendering by passing in a function.
<Route children>
: likes render props but this will always be rendered, regardless of whether the route's path matches the current location
The element rendered be the will be passed a number of props. These will be:
-
match
: object -
location
: object -
history
: object
The props
of About component in the example above will includes these objects.
Example for an URL matches a route:
URL: http://localhost:3000/about
Location's pathname or location.pathname
: /about
Route with path /about
will render About
component.
exact: bool
When true, will only match if the location.pathname
matches the route's path exactly.
<Route exact path="/about" component={About} />
Setting exact
as above means it is true.
location.pathname | route's path | exact | matches |
---|---|---|---|
/about | /about | true | yes |
/about/ | /about | true | yes |
/about/abc | /about | true | no |
/about/abc | /about | false | yes |
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
With the URL's pathname: /about
- The exact is false, both Home and About components will be rendered.
- The exact is true, About components will only be rendered.
strict: bool
When true
, a path that has a trailing slash will only match a location.pathname
with a trailing slash
<Route strict path="/about/" component={About} />
location.pathname | route's path | strict | matches |
---|---|---|---|
/about | /about/ | true | no |
/about/ | /about/ | true | yes |
/about/abc | /about | true | yes |
- Setting both
strict
andexact
are true to enforce that alocation.pathname
has no trailing slash.
<Route exact strict path="/about" component={About} />
location.pathname | route's path | matches |
---|---|---|
/about | /about | yes |
/about/ | /about | no |
/about/abc | /about | no |
A <Route>
that doesn't define a path or uses children method to render will matches with any URL's pathname.
<Route exact path="/" render={() => <h1>Home</h1>} />
<Route path="/about" render={() => <h1>About</h1>} />
<Route render={() => <h1>Page not found</h1>} />
As example above, the ` that has 'Page not found' content will always be rendered.
location.pathname | matched routes | render |
---|---|---|
/ | 1, 3 | Home and Page not found |
/about | 2, 3 | About and Page not found |
/abc | 3 | Page not found |
Using <Switch>
component to only render the first matched route.
<Switch>
<Route exact path="/" render={() => <h1>Home</h1>} />
<Route path="/about" render={() => <h1>About</h1>} />
<Route render={() => <h1>Page not found</h1>} />
</Switch>
location.pathname | matched routes | render |
---|---|---|
/ | 1 | Home |
/about | 2 | About |
/abc | 3 | Page not found |
<NavLink>
is similar to <Link>
but allow to set styling attribute to the <NavLink>
element activated or matches with current URL.
<nav>
<NavLink exact activeClassName="active" to="/">Home</NavLink>
<NavLink activeStyle={{color: 'green'}} to="/about">About</NavLink>
<NavLink activeClassName="gold"
isActive={activeFunc}
to="/contact/:id">Contact</NavLink>
<NavLink to="/contact/100">ID Link</NavLink>
</nav>
CSS file
a {
margin: 5px;
}
a, a:visited{
color: blue;
}
a.active {
color: red;
}
a.gold {
color: gold;
}
There are two way to set styling attribute for `:
- activeClassName: class
- activeStyle: styling object
By default, when a <NavLink>
is active, the class active
is apply automatically.
We don't need declare activeClassName="active"
as example above.
Declaring activeClassName="anotherClass"
if we would like to apply another class as the third <NavLink>
.
Add a function to <NavLink>
to add extra logic for determining whether the link is active by isActive
props.
const activeFunc= (match, location) => {
if (!match) {
return false;
}
//check id param from URL's pathname
const ID = parseInt(match.params.id)
return !isNaN(eventID) && eventID % 2 === 1
}
when any <NavLink>
is active, the function above will be executed to check whether the '` that is set this function is active.
Creating nested routes by define child routes within the component of parent route.
const Home = () => (<h1>Home</h1>)
const Menu = (props) => {
return (
<div>
<h1>Menu</h1>
<Link to="/menu/food">Food</Link>
<Link to="/menu/drink">Drinks</Link>
<Link to="/menu/sides">Sides</Link>
<Route path={`${props.match.url}/:section`}
render={({match}) => <h2>{match.params.section}</h2>}/>
</div>)
}
const NestedRoutesApp = (props) => (
<Router>
<div>
<Link to="/">Home</Link>
<Link to="/menu">Menu</Link>
<Route exact path="/" component={Home} />
<Route path="/menu" component={Menu} />
</div>
</Router>
)
Using props.match.url
to get matched URL's pathname of the parent route to make a relative URL.
Using `' to prompt user before navigating away from a page.
const Home = () => (<h1>Home</h1>)
class Form extends React.Component {
state = {dirty: false}
setDirty = () => this.setState({dirty: true})
render(){
return (
<div>
<h1>Form</h1>
<input type="text" onInput={this.setDirty}/>
<Prompt
when={this.state.dirty}
message="Data will be lost"
/>
</div>
)
}
}
const PromptApp = () => (
<Router>
<div>
<Link to="/">Home</Link>
<Link to="/form">Form</Link>
<Route exact path="/" component={Home} />
<Route path="/form" component={Form} />
</div>
</Router>
)
As example above, a confirmation box will show to user when having a dirty form and then navigating away Form
page.
<Prompt>
has two the following props:
When
: bool
- a condition to render <Prompt>
.
message
: string
or func
- a string or function returns a string to show a prompt to user.
Add query parameters within URL's pathname by the following two ways:
- Inline
<Link to="/?id=123&name=abc">to='/?id=123&name=abc'</Link>
Parameters string will start with question mark.
- Location object
<Link to={{pathname: '/', search: 'id=456&name=def'}}>to={location}</Link>
Get the value of query parametes by using URLSearchParams()
class.
const QueryParametersApp = (props) => (
<Router>
<div>
<Links />
<Route path="/" render={({match, location}) => (
<div>
<p>{new URLSearchParams(location.search).get('id')}</p>
<p>{new URLSearchParams(location.search).get('name')}</p>
</div>
)} />
</div>
</Router>
)
Define route's path to matches with URL parameters as following:
- Optional
<Route path="/:page?/:subpage?" render={({match}) => (
<h1>
PAGE: {match.params.page || 'Home' }<br />
SUBPAGE: {match.params.subpage}<br />
</h1>
)} />
A parameter will start with a colon and end up with a slash. The URL parameters is optional when it ends with question mark.
- Required
<Route path="/:page/:subpage" render={({match}) => (
<h1>
PAGE: {match.params.page || 'Home' }<br />
SUBPAGE: {match.params.subpage}<br />
</h1>
)} />
The route will not match with URL's pathname that doesn't have two parameters as above.
We could define a route with the path is regular expression.
const Links = () => (
<nav>
<Link to="/">/</Link>
<Link to="/17-08-2017/.html">/17-08-2017/.html</Link>
<Link to="/1-1-2017/.html">/1-1-2017/.html</Link>
</nav>
)
const regexPath = "/:a(\d{2}-\d\{2}-\d{4})/:b(\.[a-z]+)"
const RegexRouteApp = (props) => (
<Router>
<div>
<Links />
<h2>Route path: {regexPath}</h2>
<Route path="/:a(\d{2}-\d{2}-\d{4})/:b(\.[a-z]+)" render={({match}) => (
<h1>
ParamA: {match.params.a}<br />
ParamB: {match.params.b}<br />
</h1>
)} />
</div>
</Router>
)
The URL parameters will matches with the regex in parenthesis and get it's value by the name of param is front of.
A route redirects to another route:
<Route path="/new/:str"
render={({match}) => (<h1>New: {match.params.str}</h1>)} />
<Route path="/old/:str" render={({match}) => (
<Redirect to={`/new/${match.params.str}`}/>
)} />
Redirect a link that isn't defined route to another route.
const Links = () =>
<nav>
<Link to="/">Home</Link>
<Link to="/old">/old : this link is not defined route</Link>
<Link to="/new">New</Link>
</nav>
const RedirectSwitchApp = () => (
<Router>
<div>
<Links />
<Switch>
<Route exact path="/" render={() => (<h1>Home</h1>)} />
<Route path="/new" render={() => (<h1>New</h1>)} />
<Redirect from="/old" to="/new" />
</Switch>
</div>
</Router>
)
We could remove from
props from <Redirect>
to redirect all links that aren't defined routes as following:
<Switch>
<Route exact path="/" render={() => (<h1>Home</h1>)} />
<Route path="/new" render={() => (<h1>New</h1>)} />
<Redirect to="/new" />
</Switch>
<Redirect>
should be the last element in <Switch>
.
Demo branch Git: React-RouterV4/nkim
Folder: react-router
Run storybook to show demo.