理解Redux数据流

对于刚刚接触redux的前端开发者来讲,要想一会儿直接理解redux内部的数据流向可能有点不太现实。但通过一段时间的总结和上手一个简单的redux项目,相信咱们对redux内部的数据流向及内部原理就可以掌握得比较清楚了。

Redux简介

如今咱们就从Redux是什么,Redux的三大原则和Redux的核心API开始介绍Redux,并说明Redux如何与React结合使用,以及它在Flux基础上的改变。前端

Redux是什么

咱们都知道Flux它自己既不是库,也不是框架,而是一种应用的架构思想。而Redux呢,它的核心代码能够理解成一个库,但同时也强调和Flux相似的架构思想。
从设计上看,Redux参考了Flux的设计,可是对Flux许多冗余的部分作了简化,同时将函数式编程的思想融合其中。
很是有意思的是,Redux是从一个实验开始的,做者并无想到Redux会变得如此重要又被普遍使用,他只是为了经过Flux思想解决他的热重载及时间旅行的问题而已。
Redux自己很是简单,它的设计思想与React有殊途同归之妙,均是但愿用最少的API实现最核心的功能。Redux自己只把本身定位成一个可预测的状态容器
“Redux”自己指redux这个npm包,它提供若干API让咱们使用reducer建立store,并可以更新store中的数据或获取store中最新的状态。而“Redux应用”则是指使用了redux这个npm包并结合了视图层实现(如React)及其余前端应用必备组件(路由库,请求库等)组成的完整的类Flux思想的前端应用。react

Redux三大原则

想要理解Redux,必需要知道Redux设计和使用的三大原则。npm

  • 单一数据源
    在传统的MVC框架中,咱们能够根据须要建立无数个Model,而Model之间能够互相监听、触发事件甚至循环或嵌套触发事件,这些在Redux中都是不容许的。
    由于在Redux的思想里,一个应用永远只有惟一的数据源。咱们的第一反应多是:若是一个复杂应用,强制要求惟一的数据源岂不是会产生一个特别庞大的JavaScript对象。
    实际上,使用单一的数据源的好处在于整个应用状态保存在一个对象中,这样咱们随时能够提取出整个应用的状态进行持久化。此外,这样的设计也为服务端渲染提供了可能。
    至于咱们担忧的数据源对象过于庞大的问题,咱们能够经过combineReducers化解。编程

  • 状态是只读的
    在Redux中,咱们并不会本身用代码来定义一个store。取而代之的是,咱们定义一个reducer,它的功能是根据当前触发的action对当前应用的状态进行迭代,这里咱们并无直接修改应用的状态,而是返回了一份全新的状态。
    Redux提供的createStore方法会根据reducer生成store。最后,咱们能够利用store.dispatch方法来达到修改状态的目的。redux

  • 状态修改均由纯函数完成
    在Redux里,咱们经过定义reducer来确认状态的修改,而每个reducer都是纯函数,这意味着它没有反作用,即接受必定的输入,一定会获得必定的输出
    这样设计的好处不只在于reducer里对状态的修改变得简单、纯粹、可测试,更有意思的是,Redux利用每次新返回的状态生成酷炫的时间旅行调试方式,让跟踪每一次由于触发action而改变状态的结果成为了可能后端

Redux核心API

Redux的核心是一个store,这个store由Redux提供的createStore方法生成。要想生成store,必须传入reducers。
在Redux里,负责响应action并修改数据的角色是reducer。reducer本质上是一个纯函数,它有两个参数state和action。reducer的职责就是根据state和action计算出新的state。
在实际应用中,reducer在处理state时,还须要有一个特殊的非空判断。很显然,reducer第一次执行的时候,并无任何的state,而reducer的最终职责是返回新的state,所以须要在这种特殊状况下返回一个定义好的state。这也就是咱们会定义一个defaultState的缘由。
下面来介绍一下Redux中最核心的API--createStore:bash

import { createStore } from 'redux';
const store = createStore(reducers);
复制代码

经过createStore方法建立的store是一个对象,它自己包含4个方法。架构

  • getState():获取store中当前的状态。
  • dispatch(action):分发一个action,并返回这个action,这是惟一能改变store中数据的方式。
  • subscribe(listener):注册一个监听者,它在store发生变化时被调用。
  • replaceReducer(nextReducer):更新当前store里的reducer,通常只会在开发模式中调用该方法。
    在实际开发中,咱们最经常使用的是getState()和dispatch()这两个方法。至于subscribe()和replaceReducer()方法,通常会在Redux与某个系统作桥接的时候使用。

简单的项目实践

在当前的项目中,模块化开发已经成为主流。我的认为在模块化开发中,最重要的即是目录结构的设计。框架

在这里咱们对目录进行了比较细致的划分:ide

  • components:该目录下存放的是组件,好比开发网站类的项目实常常用到的Header公共组件。
  • pages: 该目录下存放的是网页,系统中各个网页放在该目录下。好比首页和详情页等。
  • store:该目录存放actions、reducers、constants(常量文件:存放dispatch要用到的常量)和index(建立store)。

我的认为在Redux项目中这样的目录设计是比较符合模块化设计思想的目录。(仁者见仁智者见智)
接下来我就介绍一下Redux具体的步骤和流程。(已请求简单列表数据为例)

第1步:reducer

store中的数据是由reducer决定的,因此第一步先完成reducer。

const defaultState = {
  homeList: []
}
export default function(state = defaultState, action) {
  switch(action.type) {
    case GET_HOME_LIST:
      return {
        homeList: action.homeList
      };
    default:
      return defaultState
  }
}
复制代码

第2步: action

export const getHomeList = {
  type: GET_HOME_LIST,
  // 模拟从后端接收到的假数据
  homeList: [0,1,2,3]
}
复制代码

第3步: constant

一个应用的常量应该是惟一的,把常量放在一个文件夹里同时按照模块划分,有利于防止变量名的冲突。

// home
export const GET_HOME_LIST = 'GET_HOME_LIST';
复制代码

第4步: index

该文件是用来建立store的。

import { createStore, 
  combineReducers} from 'redux'
import HomeReducer from './reducers/home/index';
// 多个 reducer 合并成一个
const rootReducer = combineReducers({
  home: HomeReducer
})
// 建立 store 只能接收到 一个 reducer 
// 因此 建立以前 合并一下
export default createStore(rootReducer)
复制代码

第5步: 使用store

class App extends ImmutableComponent {
  render() {
    return (
      // store成为全局变量,任何组件均可以访问了
      <Provider store={store}>
        <BrowserRouter>
            <Header />
            <Route path="/" component={Home} exact/>
            <Route path="/detail" component={Detail} />
        </BrowserRouter>
      </Provider>
    );
  }
}
复制代码
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { getHomeList } from '../../store/actions/home';
class Home extends Component {
  componentDidMount() {
    this.props.fetchHomeList();
  }
  state = {}
  render() {
    return ( <div>
      home
      length: { this.props.homeList.length }
    </div> );
  }
}
// 获取数据
// state:整个store,home页面,只要home模块,过滤一下
// 过滤完结果(return)都会由 connect 传给你组件的 props
const mapStateToProps = (state) => {
  return {
    homeList: state.home.homeList
  }
}
// 用户操做UI引发页面变化
// 发起一个action
// dispacth行为connect传给组件
const mapDispatchToProps = (dispatch) => {
  return {
    fetchHomeList() {
      dispatch(getHomeList)
    }
  }
}
export default connect(mapStateToProps, mapDispatchToProps)(Home);
复制代码

但愿这个流程能对咱们学习Redux有必定的帮助。 [注] 以上代码只是一个Redux的流程,并非完整的代码。
相关文章
相关标签/搜索