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.

Installation

Using npm:

$ npm install --save react-router-dom

Usage

Import components

import {
    BrowserRouter as Router,
    Route,
    Link,
    Switch,
    Redirect,
    Prompt,
    NavLink} from 'react-router-dom';

Examples

  • Basic Routing

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.

  • Setting exact and strict for <Route>

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 and exact are true to enforce that a location.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
  • Catch All Routes

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
  • Setting style for Link using NavLink

<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.

  • Nested Routes

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.

  • Intercepting routes change

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.

  • Query parameter

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>
)
  • URL parameters

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.

  • Regex routes

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.

  • Redirect Route

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.

⚠️ **GitHub.com Fallback** ⚠️