React Router - Install
npm install --save react-router-dom
<script
src="https://unpkg.com/react-router-dom/umd/react-router-dom.min.js"
></script>
React Router - Key Components
- BrowserRouter / HashRouter
- Route
- Link
React Router - Quick Start
import React from 'react';
import {
HashRouter as Router,
Route,
Link
} from 'react-router-dom';
React Router - Quick Start
const Home = () => (
<div>
<h2>Home</h2>
</div>
);
React Router - Quick Start
const About = () => (
<div>
<h2>About</h2>
</div>
);
React Router - Quick Start
const frameworks = [
{
name:'angular',
desc: 'Superheroic JavaScript MVW Framework'
},
{
name:'ember',
desc: 'A framework for creating ambitious web applications'
},
{
name:'react',
desc: 'A JavaScript library for building user interfaces'
}
];
React Router - Quick Start
const Framework = (props) => {
const framework = frameworks
.find(x =>
x.name === props.match.params.name
);
return (
<div>
<h3>{framework.name}</h3>
<p>{framework.desc}</p>
</div>);
};
React Router - Quick Start
const Frameworks = (props) => (
<div>
<h2>Frameworks</h2>
{frameworks.map(x =>
<div key={x.name}>
<Link
to={`${props.match.url}/${x.name}`}>
{x.name}
<Link>
</div>
)}
<hr/>
...
);
React Router - Quick Start
const Frameworks = (props) => (
<div>
...
<Route
path={`${props.match.url}/:name`}
component={Framework}
/>
<Route
exact
path={props.match.url}
render={() => (
<h3>Please select a framework.</h3>
)}
/>
</div>
);
React Router - Quick Start
const App = () => (
<HashRouter>
<div>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/about">About</Link></li>
<li><Link to="/frameworks">Frameworks</Link></li>
</ul>
<hr/>
<Route exact path="/" component={Home}/>
<Route path="/about" component={About}/>
<Route path="/frameworks" component={Frameworks} />
</div>
</HashRouter>
);
React Router - Quick Start
ReactDOM.render(
<App/>,
document.getElementById('app')
);
React Router - Redux
function humans(state = [], action) {
switch (action.type) {
case 'HUMANS_ADD':
return [
...state,
{ name: action.name }
];
}
return state;
}
React Router - Redux
function robots(state = [], action) {
switch (action.type) {
case 'ROBOTS_ADD':
return [
...state,
{
name: action.name,
manufacturer: action.manufacturer
}
];
}
return state;
}
React Router - Redux
const reducer = Redux.combineReducers({
humans,
robots
});
const store = Redux.createStore(reducer, {
humans: [
{ name: 'Leela' },
{ name: 'Fry' }
],
robots: [
{ name: 'Bender', manufacturer: 'MomCorp'}
]
});
React Router - Redux
const App = () => (
<HashRouter>
<div>
<Link to="/">
Home
</Link>
{' '}
<Link to="/humans">
Humans
<Link>
{' '}
<Link to="/robots">
Robots
</Link>
...
</div>
</HashRouter>
);
React Router - Redux
const App = () => (
<HashRouter>
<div>
...
<Route exact path="/"
component={Home}/>
<Route path="/humans"
component={HumansContainer}/>
<Route path="/robots"
component={RobotsContainer}/>
...
</div>
</HashRouter>
);
React Router - Redux
const App = () => (
<HashRouter>
<div>
...
<br/>
<StatsContainer />
</div>
</HashRouter>
);
React Router - Redux
const Home = () => (
<h2>Welcome to Futurama!</h2>
);
React Router - Redux
const HumansContainer = ReactRedux.connect(
humansMapStateToProps,
humansMapDispatchToProps
)(Humans);
React Router - Redux
const humansMapStateToProps = (state) => {
return {
list: state.humans,
};
};
const humansMapDispatchToProps = (dispatch) => {
return {
add: (human) => {
dispatch({
type: 'HUMANS_ADD',
name: human.name
});
}
};
};
React Router - Redux
const Humans = (props) => {
const items = props.list.map(item => (
<div key={item.name}>{item.name}</div>
));
return (
<div>
<h2>Humans</h2>
{items}
<br/>
<AddItem
fields={['name']}
add={props.add}
/>
</div>
);
};
React Router - Redux
const RobotsContainer = ReactRedux.connect(
robotsMapStateToProps,
robotsMapDispatchToProps
)(Robots);
React Router - Redux
const robotsMapStateToProps = (state) => {
return {
list: state.robots,
};
};
const robotsMapDispatchToProps = (dispatch) => {
return {
add: (robot) => {
dispatch({
type: 'ROBOTS_ADD',
name: robot.name,
manufacturer: robot.manufacturer
});
}
};
};
React Router - Redux
const Robots = (props) => {
const items = props.list.map(item => (
<div key={item.name}>
{item.name} [{item.manufacturer}]
</div>
));
return (
<div>
<h2>Robots</h2>
{items}
<br/>
<AddItem fields={['name', 'manufacturer']}
add={props.add} />
</div>
);
};
React Router - Redux
class AddItem extends React.Component {
...
}
React Router - Redux
constructor(props) {
super(props);
this.state = {};
props.fields.forEach(x => this.state[x] = '');
this.__change = this.__change.bind(this);
this.__add = this.__add.bind(this);
}
React Router - Redux
render() {
const inputs = this.props.fields.map(x => {
const handler = (event) => {
this.__change(x, event);
};
return <input key={x}
value={this.state[x]}
placeholder={x} onChange={handler} />;
});
return (
<div>
{inputs}
<button onClick={this.__add}>Add</button>
</div>
);
}
React Router - Redux
__change(field, event) {
const state = {};
state[field] = event.target.value;
this.setState(state);
}
React Router - Redux
__add() {
this.props.add(this.state);
const state = {};
this.props.fields.forEach(x => state[x] = '');
this.setState(state);
}
React Router - Redux
const Stats = (props) => (
<div>Humans to kill: <b>{props.toKill}</b></div>
);
React Router - Redux
const statsMapStateToProps = (state) => {
return {
toKill: state.humans.length,
};
};
const StatsContainer = ReactRedux.connect(
statsMapStateToProps
)(Stats);
React Router - Redux
ReactDOM.render(
<ReactRedux.Provider store={store}>
<App/>
</ReactRedux.Provider>,
document.getElementById('app')
);
React Router - Browser Router
const App = (props) => (
<BrowserRouter>
...
</BrowserRouter>
);
React Router - Browser Router
const express = require('express');
const path = require('path');
const app = express();
app.use(express.static('public'));
app.get('/api', ...);
app.get('/*', (req, res) => {
res.sendFile(
path.join(__dirname, 'public/index.html')
);
});
app.listen(4000, () => { console.log('Running'); });
React Router - Browser Router
basename: string
<BrowserRouter basename="/calendar"/>
<Link to="/today"/>
// renders <a href="/calendar/today">
React Router - Browser Router
getUserConfirmation: func
// this is the default behavior
const getConfirmation = (message, callback) => {
const allowTransition = window.confirm(message)
callback(allowTransition)
}
<BrowserRouter
getUserConfirmation={getConfirmation}
/>
React Router - Browser Router
forceRefresh: bool
const supportsHistory
= 'pushState' in window.history;
<BrowserRouter
forceRefresh={!supportsHistory}
/>
React Router - Browser Router
keyLength: number
// unique key in location stack
// defaults to 6
<BrowserRouter
keyLength={12}
/>
React Router - Hash Router
basename: string
getUserConfirmation: func
React Router - Hash Router
hashType: string
// defaults to "slash"
"slash" - #/sunshine/lollipops
"noslash" - #sunshine/lollipops
"hashbang" - #!/sunshine/lollipops
React Router - Link
to: string
<Link to="/courses"/>
React Router - Link
to: object
<Link to={{
pathname: '/courses',
search: '?sort=name',
hash: '#the-hash',
state: { fromDashboard: true }
}}/>
React Router - Link
replace: bool
// replace current entity in history stack
<Link to="/courses" replace />
React Router - NavLink
activeClassName: string
<NavLink
to="/faq"
activeClassName="selected"
>FAQs</NavLink>
React Router - NavLink
activeStyle: object
<NavLink
to="/faq"
activeStyle={{
fontWeight: 'bold',
color: 'red'
}}
>FAQs</NavLink>
React Router - NavLink
exact: bool
// active class/style only if exact match
<NavLink
to="/faq"
exact
activeStyle={{
fontWeight: 'bold',
color: 'red'
}}
>FAQs</NavLink>
React Router - NavLink
isActive: func
const oddEvent = (match, location) => {
if (!match) {
return false
}
const eventID = parseInt(match.params.eventID)
return !isNaN(eventID) && eventID % 2 === 1
}
<NavLink
to="/events/123"
isActive={oddEvent}
>Event 123</NavLink>
React Router - Redirect
import { Route, Redirect } from 'react-router'
<Route exact path="/" render={() => (
loggedIn ? (
<Redirect to="/dashboard"/>
) : (
<PublicHomePage/>
)
)}/>
React Router - Redirect
to: string
<Redirect to="/somewhere/else"/>
React Router - Redirect
to: object
<Redirect to={{
pathname: '/login',
search: '?utm=your+face',
state: { referrer: currentLocation }
}}/>
React Router - Redirect
push: bool
// add new entry to history, not replace
<Redirect push to="/somewhere/else"/>
React Router - Route
import {
BrowserRouter as Router,
Route
} from 'react-router-dom'
<Router>
<div>
<Route exact path="/" component={Home}/>
<Route path="/news" component={NewsFeed}/>
</div>
</Router>
React Router - Route
// route '/'
<div>
<Home/>
<!-- react-empty: 2 -->
</div>
React Router - Route
// route '/news'
<div>
<!-- react-empty: 1 -->
<NewsFeed/>
</div>
React Router - Route
// 3 render ways:
<Route component>
<Route render>
<Route children>
// all render ways will receive 3 props:
match
location
history
React Router - Route
// uses React.createElement
<Route path="/user/:username" component={User}/>
const User = ({ match }) => {
return <h1>Hello {match.params.username}!</h1>
}
React Router - Route
// convenient inline rendering
<Route path="/home"
render={() => <div>Home</div>}
/>
React Router - Route
// render even if URL is not match
<ul>
<ListItemLink to="/somewhere"/>
<ListItemLink to="/somewhere-else"/>
</ul>
const ListItemLink = ({ to, ...rest }) => (
<Route path={to} children={({ match }) => (
<li className={match ? 'active' : ''}>
<Link to={to} {...rest}/>
</li>
)}/>
)
React Router - Route
path: string
// routes without a path always match
<Route path="/users/:id" component={User}/>
React Router - Route
exact: bool
<Route exact path="/one" component={About}/>
path |
location.pathname |
exact |
matches? |
/one |
/one/two |
true |
no |
/one |
/one/two |
false |
yes |
React Router - Route
strict: bool
<Route strict path="/one/" component={About}/>
path |
location.pathname |
matches? |
/one/ |
/one |
no |
/one/ |
/one/ |
yes |
/one/ |
/one/two |
yes |
React Router - Switch
<Route path="/about" component={About}/>
<Route path="/about" component={About}/>
<Route path="/:user" component={User}/>
<Route component={NoMatch}/>
React Router - Switch
<Switch>
<Route exact path="/" component={Home}/>
<Route path="/about" component={About}/>
<Route path="/:user" component={User}/>
<Route component={NoMatch}/>
</Switch>
React Router - Switch
<Switch>
<Route exact path="/" component={Home}/>
<Route path="/users" component={Users}/>
<Redirect from="/accounts" to="/users"/>
<Route component={NoMatch}/>
</Switch>
React Router - history (prop)
// The number of entries in the history stack
length - (number)
React Router - history (prop)
// The current action (PUSH, REPLACE, or POP)
action - (string)
React Router - history (prop)
// The current location
location - (object)
React Router - history (prop)
// The path of the URL
location.pathname - (string)
React Router - history (prop)
// The URL query string
location.search - (string)
React Router - history (prop)
// The URL hash fragment
location.hash - (string)
React Router - history (prop)
// location-specific state that was provided
// e.g. push(path, state)
// only available in browser and memory history.
location.state - (string)
React Router - history (prop)
// Pushes a new entry onto the history stack
push(path, [state]) - (function)
React Router - history (prop)
// Replaces the current entry on the history stack
replace(path, [state]) - (function)
React Router - history (prop)
// Moves the pointer in the history stack by n
go(n) - (function)
React Router - history (prop)
// Equivalent to go(-1)
goBack() - (function)
React Router - history (prop)
// Equivalent to go(1)
goForward() - (function)
React Router - history (prop)
// Prevents navigation
block(prompt) - (function)
React Router - match (prop)
// Key/value pairs parsed from the URL
// corresponding to the dynamic
// segments of the path
params - (object)
React Router - match (prop)
// true if the entire URL was matched
isExact - (boolean)
React Router - match (prop)
// The path pattern used to match.
path - (string)
React Router - match (prop)
// The matched portion of the URL
url - (string)
React Router - match (prop)
matchPath('/users/123', {
path: '/users/:id',
exact: true,
strict: false
})
=>
{ isExact: true,
params: { id: "123" },
path: "/users/:id",
url: "/users/123" }
React Router - withRouter
withRouter(connect(...)(MyComponent)
React Router - withRouter
class UpdateBlocker extends React.PureComponent {
render() {
return this.props.children
}
}
React Router - withRouter
// location = { pathname: '/about' }
<UpdateBlocker>
<NavLink to='/about'>About</NavLink>
// <a href='/about' class='active'>About</a>
<NavLink to='/faq'>F.A.Q.</NavLink>
// <a href='/faq'>F.A.Q.</a>
</UpdateBlocker>
React Router - withRouter
// location = { pathname: '/faq' }
<UpdateBlocker>
// the links will not re-render,
// so they retain their previous attributes
<NavLink to='/about'>About</NavLink>
// <a href='/about' class='active'>About</a>
<NavLink to='/faq'>F.A.Q.</NavLink>
// <a href='/faq'>F.A.Q.</a>
</UpdateBlocker>
React Router - withRouter
const BlockAvoider = withRouter(UpdateBlocker)
React Router - MemoryRouter
import { MemoryRouter } from 'react-router'
/* for tests & non-browser envs */
<MemoryRouter>
<App/>
</MemoryRouter>
React Router - Deep Redux Integration
npm i react-router-redux@next
npm i history
React Router - Deep Redux Integration
// store.js
import {
createStore,
applyMiddleware,
compose } from 'redux';
import createSagaMiddleware
from 'redux-saga';
import createHistory
from 'history/createHashHistory';
import { routerMiddleware }
from 'react-router-redux';
React Router - Deep Redux Integration
// store.js
import reducer from './reducers';
import sagas from './sagas';
import initialState from './initial-state';
React Router - Deep Redux Integration
// store.js
const history = createHistory();
const sagaMiddleware = createSagaMiddleware();
let composeEnhancers = compose;
if (window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) {
composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__;
}
const store = createStore(
reducer,
initialState,
composeEnhancers(
applyMiddleware(sagaMiddleware),
applyMiddleware(routerMiddleware(history))
)
);
React Router - Deep Redux Integration
// store.js
sagaMiddleware.run(sagas);
store.history = history;
export default store;
React Router - Deep Redux Integration
// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { ConnectedRouter } from 'react-router-redux';
import { Provider } from 'react-redux';
import Store from './store';
import App from './containers/app';
const Root = ({ store }) => (
<Provider store={store}>
<ConnectedRouter history={store.history}>
<App />
</ConnectedRouter>
</Provider>
);
ReactDOM.render(<Root store={Store}/>, document.getElementById('app'));
React Router - Deep Redux Integration
// dispatchers/goto.js
import { push } from 'react-router-redux';
export default (Actions, route) =>
dispatch =>
name => {
dispatch( Actions.select({ name }) );
dispatch( push(`${route}/${name}`) );
};
React Router - Deep Redux Integration
// sagas/admins/create.js
import { put, ... } from 'redux-saga/effects';
import { push } from 'react-router-redux';
export default function * () {
...
yield put(push(`/dashboard/admins/${login}`));
...
}
React Router - Deep Redux Integration
// sagas/vfs/create.js
import { select, ... } from 'redux-saga/effects';
export default rootRoute => function * () {
...
const { router: { location: { pathname } } }
= yield select();
if (pathname !== rootRoute
&& pathname !== rootRoute + '/'
) {
name = `${pathname
.replace(rootRoute + '/', '')}
/${name}`;
}
...
}