结合 RubyChina React 项目讲解 Redux 原理

Redux 原理解析

Posted by Rina on 2017-05-22

打赏支持

项目Demo: http://ruby-china.liuzhen.me

项目地址: https://github.com/liuzhenangel/react-ruby-china

Redux 的工作流程:

流程说明:

当用户访问 http://ruby-china.liuzhen.me 时, 程序走到 index.html 页面, 在 index.js 文件中

const middleware = [ thunk ]
if (process.env.NODE_ENV !== 'production') {
  middleware.push(createLogger())
}

const store = createStore(
  reducer,
  applyMiddleware(...middleware)
)
ReactDOM.render(
  <Provider store={store}>
    {Route}
  </Provider>,
  document.getElementById('root')
);

这里的Provider是将store参数传递给下面的组件, 这个组件由Route决定, Route的配置在 src/Config/Route.js 文件中,

<Router onUpdate={() => window.scrollTo(0, 0)} history={browserHistory}>
 <Route path="/" component={App}>
  <IndexRoute component={Home} />
  <Route path="topics" component={Topics}/>
 </Route>
</Router>

Router 中的onUpdate可以用来处理点击一个链接后跳转到页面的顶部. history会记住历史浏览记录. <Route path="/" component={App}> 这是个嵌套路由, 指定根目录, App是layout用于页面布局.所以访问 /topics 时会加载 <App><Topics /></App> IndexRoute是当访问 / 目录里默认去哪个组件. 这里设置的是Home, 这个没有path参数. Route有path, component两个参数. path配置路径, component配置组件.

根据Route取到对应的组件, 然后通过 document.getElementById('root') 找到index.html页面上的位置, 再由ReactDOM.render把内容插入到#root这个节点下面.

在 Home 这个组件里定义了 constructor 函数, 这里面调用了 this.props.actions.fetchTopics({type: 'excellent'}) 这里的actions参数是怎么来的呢.

根据路由配置会加载App.js组件, 这个组件里 export default connect(mapStateToProps, mapDispatchToProps)(App) 这个操作把 所有的 actions 和 state 都转换成 props 传递到子组件下. 所以 Home 组件里的props才有actions这个参数. 当用户执行 this.props.actions.fetchTopics({type: 'excellent'}) 这条语句的时候等价于 store.dispatch(fetchTopics({type: 'excellent'})). 当dispatch 触发时, reducer 会自动执行, 根据 fetchTopics 函数返回的结果, 计算出一个新的state, 再通过 Provider 将参数传递到组件中, 组件就会更新UI.

这里的数据请求用的是异步数据调用, 数据结果还没有取回的时候 fetchTopics 就已经结束并执行完 reducer 了, 那异步数据请求完之后怎么再去更新state呢, 这就是为什么要在 src/index.js 文件中的 createStore() 加了个参数 thunk, 在 fetchTopics 里返回了一个函数, 正常情况下 dispatch 只能接受对象做为参数. thunk 就是让 dispath 能接受函数做为一个参数. 在这个返回的函数里当异步数据请求成功后, 调用了dispatch这个函数, 所以异常与同步不同的是异常请求会调用两次dispatch, 在用户触发时和请求成功后. 调用sispatch后, reducer就会自动去计算action的值返回一个新的state, 再由Provider把这个更新过的store下发到组件中, 组件再更新页面.

结合流程图, 解释一下几个概念:

1. Store 是用来存放数据. 在项目的 src/index.js 文件中

import { createStore, applyMiddleware } from 'redux'

const middleware = [ thunk ]
if (process.env.NODE_ENV !== 'production') {
  middleware.push(createLogger())
}

const store = createStore(
  reducer,
  applyMiddleware(...middleware)
)

这个由 redux 提供的 createStore 函数创建了store对象, 这个store对像就是用来存储数据. 这里的 applyMiddleware 是个中间件, 这个中间件接受 thunk, createLogger 参数. reducer作为参数传入createStore, 当触发dispatch时会自动计算reducer, 然后返回一个新的state.

2. Action

action 是一系列动作, 当用户触发action后就会导致state的变化, state一旦更新, view也会发生相应的变化. 比如: 当用户在访问 http://ruby-china.liuzhen.me 的时候点击打开某一篇文章, 在操作这个动作时就是触发一个action, 这个action会向 RubyChina 服务器请求相就的数据. Action 文件点击 这里 查看

3. Component

component 是UI组件, UI组件只负责UI的呈现,不管业务逻辑, 所有参数由this.props提供.

4. Reducers

文件 src/Reducer/index.js 就是 Reducer 的实现. 这个函数接受两个参数, 一个是当前的 State, 一个是Action, 这里的 action 参数 就是调用Action后, Action里的dispatch会触发Reducer自动执行, 然后返回一个新的state, 这行代码当发现store的数据更新后, 会自动下发到下面的组件, 组件拿到这个参数后就会更新UI.