沿着上一部分 Redux With React,在利用 redux
与 react
构建了单个视图的 WebApp,这部分介绍 redux
与 react-router
结合实现多个视图的 WebApp,代码也是在上一部分的基础上做修改。
react-router
提供了 react
的路由机制,除了这个库之外还有另外一个库 react-router-redux
用官方文档的话来说,首先 redux
可以和 react-router
两个一起使用就可以解决路由的问题,但是如果考虑到一些辅助的功能(例如和 Redux DevTool 等一起使用),就很有必要使用 react-router-redux
了。而且其实两者的结合非常的简单,我这里就先把它加上了。
$ npm install --save react-router react-router-redux
在不改变之前单个 VisibleCounter
的视图的前提下引入 router
。首先看一下代码的目录结构。
.
├── actions
│ └── index.js
├── components
│ ├── App.js
│ └── Counter.js
├── containers
│ └── VisibleCounter.js ++
├── dist
│ ├── bundle.js
│ ├── index.html
│ └── styles.css
├── entry.js ++
├── package.json
├── reducers
│ ├── counter.js
│ └── index.js ++
├── routes.js ++
├── styles
│ ├── index.scss
│ └── theme.scss
└── webpack.config.js
其中新添加或者是有修改的文件都用 ++
做了标记,可以看到作为 components(presentational) 在引入其他的组件的时候并没有收到影响,而 containers 则会因为 store
的变化而变化。
为了支持 router
需要做这么几件事情:
用 react-router
提供的 Route
标签声明路由结构
这里就是一个路由 /
对应组件 App
,而 App
里面包含一个 VisibleCounter
routes.js
:
import React from 'react';
import { Route } from 'react-router';
import App from './components/App';
export default (
<Route path="/" component={App}></Route>
)
在原有的 reducer
中添加 react-router-redux
所提供的路由的 reducer
,这里通过 redux
所提供的 combineReducers
实现。
reducers/index.js
:
import counter from './counter';
import { routerReducer as routing } from 'react-router-redux';
import { combineReducers } from 'redux';
export default combineReducers({
counter,
routing
});
由于 reducer
做了调整,那么在 VisibleCounter
与 store
链接时也会有改变,在下面的代码中我用 ++
标识修改的部分
containers/VisibleCounter.js
:
import { connect } from 'react-redux';
import Counter from "../components/Counter";
import { increment, decrement } from '../actions/index';
function mapStatToProps(state) {
return {
value: state.counter //++
}
}
function mapDispatchToProps(dispatch) {
return {
onIncrement: () => {
dispatch(increment())
},
onDecrement: () => {
dispatch(decrement())
}
}
}
const VisibleCounter = connect(
mapStatToProps,
mapDispatchToProps
)(Counter);
export default VisibleCounter;
修改入口,用 react-router
所提供的 <Router>
标签包装整个应用,并以属性的方式传递路由结构(routes
)和所需要的浏览器历史(history
)支持(hash
或者是 browser
)
entry.js
:
require('./styles/index.scss');
import React from "react";
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { hashHistory, Router } from 'react-router';
import { syncHistoryWithStore } from 'react-router-redux';
import { createStore } from 'redux';
import reducer from './reducers/index';
import routes from './routes';
const store = createStore(reducer);
const history = syncHistoryWithStore(hashHistory, store);
const rootEl = document.querySelector("#root");
function render() {
ReactDOM.render(
<Provider store={store}>
<Router routes={routes} history={history}/>
</Provider>,
rootEl
)
}
render();
store.subscribe(render);
现在添加一个新的视图 About
containers/About.js
:
import React, { Component, PropTypes } from 'react';
class About extends Component {
render() {
return (
<p>
This is about page.
</p>
)
}
}
export default About;
拆分 App
和 VisibleCounter
使得 App
作为支持所有子视图的框架
containres/App.js
:
import React, { Component } from 'react';
class App extends Component {
render() {
const { children } = this.props.children;
return (
<div>
{ children }
</div>
)
}
}
export default App;
更新路由,添加 /
下的两个子路由 counter
和 about
routes.js
:
import React from 'react';
import { Route, IndexRoute } from 'react-router';
import App from './components/App';
import VisibleCounter from './containers/VisibleCounter';
import About from './components/About';
export default (
<Route path="/" component={App}>
<IndexRoute component={VisibleCounter}/>
<Route path="/counter" component={VisibleCounter}/>
<Route path="/about" component={About}/>
</Route>
)
其中 IndexRoute
表明 VisibleCounter
为在 /
时的默认路由。
在执行 npm start
之后看看 http://localhost:8080/
是不是计数器?http://localhost:8080/#/about
是不是我们的 About
页面?
这里讲解了基本的路由的构建,当然路由还不仅仅有这些内容啦,还有 <Link>
pushState
等等内容,其中 <Link>
的内容可以到 https://github.com/reactjs/react-router-tutorial
来看,然后其他部分在以后遇到的时候再解释。