• 搭配 React Router
    • 安装 React Router
    • 配置后备(fallback) URL
      • 使用 Create React App 须知
      • 配置 Express
      • 配置 WebpackDevServer
    • 连接 React Router 和 Redux 应用
      • components/Root.js
  • 通过 React Router 导航
    • containers/FilterLink.js
    • components/Footer.js
  • 从 URL 中读取数据
    • containers/VisibleTodoList.js
    • components/App.js
  • 下一步
    • 其它路由库注意点

    搭配 React Router

    现在你想在你的 Redux 应用中使用路由功能,可以搭配使用 React Router 来实现。
    Redux 和 React Router 将分别成为你数据和 URL 的事实来源(the source of truth)。
    在大多数情况下, 最好 将他们分开,除非你需要时光旅行和回放 action 来触发 URL 改变。

    安装 React Router

    可以使用 npm 来安装 React Router。本教程基于 react-router@^2.7.0

    npm install --save react-router

    配置后备(fallback) URL

    在集成 React Router 之前,我们需要配置一下我们的开发服务器。
    显然,我们的开发服务器无法感知配置在 React Router 中的 route。
    比如:你想访问并刷新 /todos,由于是一个单页面应用,你的开发服务器需要生成并返回 index.html
    这里,我们将演示如何在流行的开发服务器上启用这项功能。

    使用 Create React App 须知

    如果你是使用 Create React App (你可以点击这里了解更多,译者注)工具来生成项目,会自动为你配置好后备(fallback) URL。

    配置 Express

    如果你使用的是 Express 来返回你的 index.html 页面,可以增加以下代码到你的项目中:

    1. app.get('/*', (req,res) => {
    2. res.sendfile(path.join(__dirname, 'index.html'))
    3. })

    配置 WebpackDevServer

    如果你正在使用 WebpackDevServer 来返回你的 index.html 页面,
    你可以增加如下配置到 webpack.config.dev.js:

    1. devServer: {
    2. historyApiFallback: true,
    3. }

    连接 React Router 和 Redux 应用

    在这一章,我们将使用 Todos 作为例子。我们建议你在阅读本章的时候,先将仓库克隆下来。

    首先,我们需要从 React Router 中导入 <Router /><Route />。代码如下:

    1. import { Router, Route, browserHistory } from 'react-router';

    在 React 应用中,通常你会用 <Router /> 包裹 <Route />
    如此,当 URL 变化的时候,<Router /> 将会匹配到指定的路由,然后渲染路由绑定的组件。
    <Route /> 用来显式地把路由映射到应用的组件结构上。
    path 指定 URL,用 component 指定路由命中 URL 后需要渲染的那个组件。

    1. const Root = () => (
    2. <Router>
    3. <Route path="/" component={App} />
    4. </Router>
    5. );

    另外,在我们的 Redux 应用中,我们仍将使用 <Provider />
    <Provider /> 是由 React Redux 提供的高阶组件,用来让你将 Redux 绑定到 React (详见 搭配 React)。

    然后,我们从 React Redux 导入 <Provider />

    1. import { Provider } from 'react-redux';

    我们将用 <Provider /> 包裹 <Router />,以便于路由处理器可以访问 store(暂时未找到相关中文翻译,译者注)。

    1. const Root = ({ store }) => (
    2. <Provider store={store}>
    3. <Router>
    4. <Route path="/" component={App} />
    5. </Router>
    6. </Provider>
    7. );

    现在,如果 URL 匹配到 ‘/‘,将会渲染 <App /> 组件。此外,我们将在 ‘/‘ 后面增加参数 (:filter),
    当我们尝试从 URL 中读取参数 (:filter),需要以下代码:

    1. <Route path="/(:filter)" component={App} />

    也许你想将 ‘#’ 从 URL 中移除(例如:http://localhost:3000/#/?_k=4sbb0i)。
    你需要从 React Router 导入 browserHistory 来实现:

    1. import { Router, Route, browserHistory } from 'react-router';

    然后将它传给 <Router /> 来移除 URL 中的 ‘#’:

    1. <Router history={browserHistory}>
    2. <Route path="/(:filter)" component={App} />
    3. </Router>

    只要你不需要兼容古老的浏览器,比如IE9,你都可以使用 browserHistory

    components/Root.js

    1. import React, { PropTypes } from 'react';
    2. import { Provider } from 'react-redux';
    3. import { Router, Route, browserHistory } from 'react-router';
    4. import App from './App';
    5. const Root = ({ store }) => (
    6. <Provider store={store}>
    7. <Router history={browserHistory}>
    8. <Route path="/(:filter)" component={App} />
    9. </Router>
    10. </Provider>
    11. );
    12. Root.propTypes = {
    13. store: PropTypes.object.isRequired,
    14. };
    15. export default Root;

    通过 React Router 导航

    React Router 提供了 <Link /> 来实现导航功能。
    下面将举例演示。现在,修改我们的容器组件 <FilterLink /> ,这样我们就可以使用 <FilterLink /> 来改变 URL。你可以通过 activeStyle 属性来指定激活状态的样式。

    1. import React from 'react';
    2. import { Link } from 'react-router';
    3. const FilterLink = ({ filter, children }) => (
    4. <Link
    5. to={filter === 'all' ? '' : filter}
    6. activeStyle={{
    7. textDecoration: 'none',
    8. color: 'black'
    9. }}
    10. >
    11. {children}
    12. </Link>
    13. );
    14. export default FilterLink;
    1. import React from 'react'
    2. import FilterLink from '../containers/FilterLink'
    3. const Footer = () => (
    4. <p>
    5. Show:
    6. {" "}
    7. <FilterLink filter="all">
    8. All
    9. </FilterLink>
    10. {", "}
    11. <FilterLink filter="active">
    12. Active
    13. </FilterLink>
    14. {", "}
    15. <FilterLink filter="completed">
    16. Completed
    17. </FilterLink>
    18. </p>
    19. );
    20. export default Footer

    这时,如果你点击 <FilterLink />,你将看到你的 URL 在 '/complete''/active''/' 间切换。
    甚至还支持浏览的回退功能,可以从历史记录中找到之前的 URL 并回退。

    从 URL 中读取数据

    现在,即使 URL 改变,todo 列表也不会被过滤。
    这是因为我们是在 <VisibleTodoList />mapStateToProps() 函数中过滤的。
    这个目前仍然是和 state 绑定,而不是和 URL 绑定。
    mapStateToProps 的第二可选参数 ownProps,这个是一个传递给 <VisibleTodoList /> 所有属性的对象。

    containers/VisibleTodoList.js

    1. const mapStateToProps = (state, ownProps) => {
    2. return {
    3. todos: getVisibleTodos(state.todos, ownProps.filter) // 以前是 getVisibleTodos(state.todos, state.visibilityFilter)
    4. };
    5. };

    目前我们还没有传递任何参数给 <App />,所以 ownProps 依然是一个空对象。
    为了能够根据 URL 来过滤我们的 todo 列表,我们需要向 <VisibleTodoList /> 传递URL参数。

    之前我们写过:<Route path="/(:filter)" component={App} />,这使得可以在 App 中获取 params 的属性。

    params 是一个包含 url 中所有指定参数的对象。
    例如:如果我们访问 localhost:3000/completed,那么 params 将等价于 { filter: 'completed' }
    现在,我们可以在 <App /> 中读取 URL 参数了。

    注意,我们将使用 ES6 的解构赋值来对 params 进行赋值,以此传递给 <VisibleTodoList />

    components/App.js

    1. const App = ({ params }) => {
    2. return (
    3. <div>
    4. <AddTodo />
    5. <VisibleTodoList
    6. filter={params.filter || 'all'}
    7. />
    8. <Footer />
    9. </div>
    10. );
    11. };

    下一步

    现在你已经知道如何实现基础的路由,接下来你可以阅读 React Router API 来学习更多知识。

    其它路由库注意点

    Redux Router 是一个实验性质的库,它使得你的 URL 的状态和 redux store 内部状态保持一致。它有和 React Router 一样的 API,但是它的社区支持比 react-router 小。

    React Router Redux 将你的 redux 应用和 react-router 绑定在一起,并且使它们保持同步。如果没有这层绑定,你将不能通过时光旅行来回放 action。除非你需要这个,不然 React-router 和 Redux 完全可以分开操作。