[react] react-router、 react-router-redux
import React from 'react';
import ReactDOM from 'react-dom';
import { Router, Route, hashHistory, IndexRoute } from 'react-router';
import App from './components/App';
import Home from './components/Home';
import Repos from './components/Repos';
import About from './components/About';
import User from './components/User';
import Contacts from './components/Contacts';
ReactDOM.render(
<Router history={hashHistory}>
<Route path="/" component={App}>
<IndexRoute component={Home} />
<Route path="/repos/:name" component={Repos} />
<Route path="/about" component={About} />
<Route path="/user" component={User} />
<Route path="/contacts" component={Contacts} />
</Route>
</Router>,
document.getElementById('app'));
/* 另外一种写法
const routes = (
<Route path="/" component={App}>
<IndexRoute component={Home} />
<Route path="/repos/:name" component={Repos} />
<Route path="/about" component={About} />
<Route path="/user" component={User} />
<Route path="/contacts" component={Contacts} />
</Route>
);
ReactDOM.render(
<Router routes={routes} history={hashHistory} />,
document.getElementById('app'));
*/
react-router
<Router>
Primary component of React Router.
It keeps your UI and the URL in sync.
Props
children (required)
One or many
When the history changes,
and render their configured components, with child route components nested inside the parents.
routes
Alias for children.
history
The history the router should listen to.
Typically browserHistory or hashHistory.
import { browserHistory } from 'react-router'
ReactDOM.render(<Router history={browserHistory} />, el)
createElement(Component, props)
When the router is ready to render a branch of route components,
it will use this function to create the elements.
You may want to take control of creating the elements when you're using some sort of data abstraction,
like setting up subscriptions to stores, or passing in some sort of application module to each component via props.
<Router createElement={createElement} />
// default behavior
function createElement(Component, props) {
// make sure you pass all the props in!
return <Component {...props} />
}
// maybe you're using something like Relay
function createElement(Component, props) {
// make sure you pass all the props in!
return <RelayContainer Component={Component} routerProps={props} />
}
onError(error)
While the router is matching, errors may bubble up, here is your opportunity to catch and deal with them. Typically these will come from async features like route.getComponents, route.getIndexRoute, and route.getChildRoutes.
onUpdate()
Called whenever the router updates its state in response to URL changes.
render(props)
This is primarily for integrating with other libraries that need to participate in rendering before the route components are rendered.
It defaults to render={(props) => <RouterContext {...props} />}
.
Ensure that you render a
<Link>
The primary way to allow users to navigate around your application. <Link>
will render a fully accessible anchor tag with the proper href.
A <Link>
can know when the route it links to is active and automatically apply an activeClassName
and/or activeStyle
when given either prop. The <Link>
will be active if the current route is either the linked route or any descendant of the linked route. To have the link be active only on the exact linked route, use <IndexLink>
instead or set the onlyActiveOnIndex
prop.
Props
to
A location descriptor. Usually this is a string or an object, with the following semantics:
- If it's a string it represents the absolute path to link to, e.g.
/users/123
(relative paths are not supported). - If it's an object it can have four keys:
pathname
: A string representing the path to link to.query
: An object of key:value pairs to be stringified.hash
: A hash to put in the URL, e.g.#a-hash
.state
: State to persist to thelocation
.
Note: React Router currently does not manage scroll position, and will not scroll to the element corresponding to hash
.
activeClassName
The className a <Link>
receives when its route is active. No active class by default.
activeStyle
The styles to apply to the link element when its route is active.
onClick(e)
A custom handler for the click event. Works just like a handler on an <a>
tag - calling e.preventDefault()
will prevent the transition from firing, while e.stopPropagation()
will prevent the event from bubbling.
onlyActiveOnIndex
If true
, the <Link>
will only be active when the current route exactly matches the linked route.
others
You can also pass props you'd like to be on the <a>
such as a title
, id
, className
, etc.
Example
Given a route like <Route path="/users/:userId" />
:
<Link to={`/users/${user.id}`} activeClassName="active">{user.name}</Link>
// becomes one of these depending on your History and if the route is
// active
<a href="/users/123" class="active">Michael</a>
<a href="#/users/123">Michael</a>
// change the activeClassName
<Link to={`/users/${user.id}`} activeClassName="current">{user.name}</Link>
// change style when link is active
<Link to="/users" style={{color: 'white'}} activeStyle={{color: 'red'}}>Users</Link>
<Route>
A <Route>
is used to declaratively map routes to your application's
component hierarchy.
Props
path
The path used in the URL.
It will concat with the parent route's path unless it starts with /
,
making it an absolute path.
Note: Absolute paths may not be used in route config that is dynamically loaded.
If left undefined, the router will try to match the child routes.
component
A single component to be rendered when the route matches the URL. It can
be rendered by the parent route component with this.props.children
.
const routes = (
<Route component={App}>
<Route path="groups" component={Groups} />
<Route path="users" component={Users} />
</Route>
)
class App extends React.Component {
render () {
return (
<div>
{/* this will be either <Users> or <Groups> */}
{this.props.children}
</div>
)
}
}
components
Routes can define one or more named components as an object of [name]: component
pairs to be rendered when the path matches the URL. They can be rendered by the parent route component with this.props[name]
.
// Think of it outside the context of the router - if you had pluggable
// portions of your `render`, you might do it like this:
// <App main={<Users />} sidebar={<UsersSidebar />} />
const routes = (
<Route component={App}>
<Route path="groups" components={{main: Groups, sidebar: GroupsSidebar}} />
<Route path="users" components={{main: Users, sidebar: UsersSidebar}}>
<Route path=":userId" component={Profile} />
</Route>
</Route>
)
class App extends React.Component {
render () {
const { main, sidebar } = this.props
return (
<div>
<div className="Main">
{main}
</div>
<div className="Sidebar">
{sidebar}
</div>
</div>
)
}
}
class Users extends React.Component {
render () {
return (
<div>
{/* if at "/users/123" `children` will be <Profile> */}
{/* UsersSidebar will also get <Profile> as this.props.children,
so its a little weird, but you can decide which one wants
to continue with the nesting */}
{this.props.children}
</div>
)
}
}
getComponent(nextState, callback)
Same as component
but asynchronous, useful for code-splitting.
callback
signature
cb(err, component)
<Route path="courses/:courseId" getComponent={(nextState, cb) => {
// do asynchronous stuff to find the components
cb(null, Course)
}} />
getComponents(nextState, callback)
Same as components
but asynchronous, useful for
code-splitting.
callback
signature
cb(err, components)
<Route path="courses/:courseId" getComponents={(nextState, cb) => {
// do asynchronous stuff to find the components
cb(null, {sidebar: CourseSidebar, content: Course})
}} />
children
Routes can be nested, this.props.children
will contain the element created from the child route component. Please refer to the Route Configuration since this is a very critical part of the router's design.
onEnter(nextState, replace, callback?)
Called when a route is about to be entered. It provides the next router state and a function to redirect to another path. this
will be the route instance that triggered the hook.
If callback
is listed as a 3rd argument, this hook will run asynchronously, and the transition will block until callback
is called.
callback
signature
cb(err)
const userIsInATeam = (nextState, replace, callback) => {
fetch(...)
.then(response = response.json())
.then(userTeams => {
if (userTeams.length === 0) {
replace(`/users/${nextState.params.userId}/teams/new`)
}
callback();
})
.catch(error => {
// do some error handling here
callback(error);
})
}
<Route path="/users/:userId/teams" onEnter={userIsInATeam} />
onChange(prevState, nextState, replace, callback?)
Called on routes when the location changes, but the route itself neither enters or leaves. For example, this will be called when a route's children change, or when the location query changes. It provides the previous router state, the next router state, and a function to redirect to another path. this
will be the route instance that triggered the hook.
If callback
is listed as a 4th argument, this hook will run asynchronously, and the transition will block until callback
is called.
onLeave(prevState)
Called when a route is about to be exited.
Histories
React Router is built with history.
In a nutshell, a history knows how to listen to the browser's address
bar for changes and parses the URL into a location
object that the
router can use to match routes and render the correct set of components.
You import them from the React Router package:
// JavaScript module import
import { browserHistory } from 'react-router'
Then pass them into your <Router>
:
render(
<Router history={browserHistory} routes={routes} />,
document.getElementById('app')
)
browserHistory
Browser history is the recommended history for browser application with React Router. It uses the History API built into the browser to manipulate the URL, creating real URLs that look like example.com/some/path
.
Configuring Your Server
Your server must be ready to handle real URLs. When the app first loads at /
it will probably work, but as the user navigates around and then hits refresh at /accounts/23
your web server will get a request to /accounts/23
. You will need it to handle that URL and include your JavaScript application in the response.
An express app might look like this:
const express = require('express')
const path = require('path')
const port = process.env.PORT || 8080
const app = express()
// serve static assets normally
app.use(express.static(__dirname + '/public'))
// handle every other route with index.html, which will contain
// a script tag to your application's JavaScript file(s).
app.get('*', function (request, response){
response.sendFile(path.resolve(__dirname, 'public', 'index.html'))
})
app.listen(port)
console.log("server started on port " + port)
If you're using nginx, use the try_files
directive:
server {
...
location / {
try_files $uri /index.html;
}
}
This lets nginx serve static asset files and serves your index.html
file when another file isn't found on the server.
There is also a similar approach for Apache servers. Create an .htaccess
file in your folder's root:
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]
IE8, IE9 Support
react-router-redux
history = syncHistoryWithStore(history, store, [options])
Creates an enhanced history from the provided history. This history changes history.listen
to pass all location updates through the provided store first. This ensures if the store is updated either from a navigation event or from a time travel action, such as a replay, the listeners of the enhanced history will stay in sync.
You must provide the enhanced history to your <Router>
component. This ensures your routes stay in sync with your location and your store at the same time.
The options
object takes in the following optional keys:
selectLocationState
- (defaultstate => state.routing
) A selector function to obtain the history state from your store. Useful when not using the providedrouterReducer
to store history state. Allows you to use wrappers, such as Immutable.js.adjustUrlOnReplay
- (defaulttrue
) Whenfalse
, the URL will not be kept in sync during time travel. This is useful when usingpersistState
from Redux DevTools and not wanting to maintain the URL state when restoring state.
import React from 'react'
import ReactDOM from 'react-dom'
import { createStore, combineReducers, applyMiddleware } from 'redux'
import { Provider } from 'react-redux'
import { Router, Route, browserHistory } from 'react-router'
import { syncHistoryWithStore, routerReducer } from 'react-router-redux'
import reducers from '<project-path>/reducers'
// Add the reducer to your store on the `routing` key
const store = createStore(
combineReducers({
...reducers,
routing: routerReducer
})
)
// Create an enhanced history that syncs navigation events with the store
const history = syncHistoryWithStore(browserHistory, store)
ReactDOM.render(
<Provider store={store}>
{ /* Tell the Router to use our enhanced history */ }
<Router history={history}>
<Route path="/" component={App}>
<Route path="foo" component={Foo}/>
<Route path="bar" component={Bar}/>
</Route>
</Router>
</Provider>,
document.getElementById('mount')
)
push(location)
, replace(location)
, go(number)
, goBack()
, goForward()
You must install routerMiddleware
for these action creators to work.
Action creators that correspond with the [history methods of the same name]. For reference they are defined as follows:
push
- Pushes a new location to history, becoming the current location.replace
- Replaces the current location in history.go
- Moves backwards or forwards a relative number of locations in history.goForward
- Moves forward one location. Equivalent togo(1)
goBack
- Moves backwards one location. Equivalent togo(-1)
Both push
and replace
take in a [location descriptor], which can be an object describing the URL or a plain string URL.
These action creators are also available in one single object as routerActions
, which can be used as a convenience when using Redux's bindActionCreators()
.
routerMiddleware(history)
A middleware you can apply to your Redux store
to capture dispatched actions created by the action creators. It will redirect those actions to the provided history
instance.
LOCATION_CHANGE
An action type that you can listen for in your reducers to be notified of route updates. Fires after any changes to history.