聊一聊Redux在React项目中的数据管理实现方式

前言

     在两年前曾短暂的使用过react+redux进行开发,但是一入vue深似海,今后其余是路人,趁着闲的时间,赶忙撸一把redux在react的实现。下面来简单的和你们分享一下。html

什么是flux?

     在了解redux以前,有必要先了解一下flux, flux是Facebook用户创建客户端Web应用的前端架构,以单向数据流方式支持MVC, Flux应用有三个主要部分:Dispatcher调度,存储Store 和 视图View ,当一个用户和视图交互时,有一个分发器dispatcher用来发送动做action到数据存储中,而后更新视图,存储接受更新,适当地调节这些更新,而不是一致地依赖外部更新其数据,存储以外根本不知道它是如何管理领域数据的,这有助于实现一种清晰的分离关注前端

这是flux的结构和数据流向图,不难看出一个单向数据流是Flux模式的核心。vue

什么是redux?

     你们若是稍有了解或者曾经接触的话,必定会听过这样一句话:若是说flux是一种思想,那么redux就是其中一种实现。 固然还有其它的实现方式如MoBx等。react

     Redux是对flux的一种演变,也是flux思想的一种子集。设计目的在于 Redux 试图让 state 的变化变得可预测,Redux使Javascript的状态管理变得更加可预期,以一种新方式思考开发应用,这个方式是:状态从一个初始状态开始,被一系列动做序列改变,这种新方式是通往复杂Web应用的捷径。ios

    这张图很详细的解释了redux的数据流向,对比flux能够看出,redux依旧是经过action来描述将要触发的行为, 没有dispatcher这个分发器了,当每次Action被触发须要dispatch时,使用一个函数称为reducer来返回新的应用状态。 redux保留的应用状态是不可变的。有兴趣能够了解一下 immutable.js这个概念。

redux的工做流程

redux的工做流程与其设计三大基本原则密不可分json

  • 单一数据源
  • state是只读的
  • 只能使用reducer来修改

首先要明确一下在react中完整的redux流程须要那几个对象 或者角色。redux

  1. 组件/视图
  2. state 存储中心
  3. action 行为
  4. reducer 暂且叫它处理业务的地方或者过滤器

再来看它的工做原理axios

     上述 2,3,4的合集咱们暂且将它称为store, 基本的角色有了,那么还得作一些角色之间的关联,好比组件,若是须要访问store里面的数据,那么首先就要和store作连接,只有创建链接以后那么他才有一个访问权。若是组件想要访问store里面的state,那么而后他要定义一个动做类型,包含一个名称和一些便于store识别的信息(可选),根据这个动做类型就是action, store会把他丢给reducer, reducer是store的大脑,能够访问store中的元数据,而后根据store丢过来的信息,reducer作出种种处理,最后返回一个结果。这个结果再由store传递给视图方面,来使页面更新。api

若是上面的这个阐述的不够清楚的话,你们能够想象一个模拟借书的场景 把各个角色代入进去,大体是这样的,react-router

component/view ====》 借书人小王

state====》图书馆A

action====》是欲借的书的信息(三国演义)

reducer====> 图书馆管理员(易中天)

角色暂时这样安放,那么把场景带入,

     话说中国历史博大精深,小王同窗从小最爱学历史,并且对三国的历史最感兴趣,因而乎小王周五放学拿着借书卡就往国图书馆奔, (ps:借书人首先要在该图书馆办一个借书卡,图书馆见卡才能放行) 走到门口,门卫一看有借书卡,来的仍是个小伙子,还夸了句:“小伙子年纪轻轻就这么爱学习,好啊 真好啊。” 小王抿嘴一笑, 不置能否,径直借书去了,但是走进去,小王蒙了,这么多书,我找一本三国演义,我太难了,此时小王灵机一动突然想起,这里头的图书馆管理员,易中天,江湖人称老易,掌管整个书馆,绝对能找到,因而找到老易,老易是一个七八十岁老头,一脸博学的文化人,看见小王也很高兴,说小伙子,这回借什么书,小王说:“易爷爷,此次想借一本三国演义”,老易说:“三国演义啊,行 等我想一想在哪”,约莫4,5秒,老易径自拿出一本三国演义给了小王,小王大喜连声道谢。因而捧书而走。 埋头苦读,遂成三国通。

整个场景与上面的阐述你们能够思考一下,上面主要是讲一下概念,你们最关心的确定是代码实现 下面分步骤来解释.

代码实现

拿简书网站的这个部分来演示一下实现

1. 定义组件

这是定义的上面那个列表的组件,那么组件写好以后,咱们就开始往里头填数据,数据从何而来,天然是redux

2.定义元数据

reducer.js 内容以下:
import { fromJS } from 'immutable';
import * as constants from './constants';
const defaultState = fromJS({
	writerList: [],
});
复制代码

这个writerList就是咱们最后要渲染到WriterWrapper里面的数据,接下来的一切行为都是为了取到这个writerList

3.连接组件与store

WriterWrapper组件添加以下内容
import React, { PureComponent } from 'react';
import {connect} from 'react-redux'
const mapStateToProps = (state) =>{
	return {
           list: state.getIn(['home', 'writerList'])   // 这一步取到为空 动做还未触发
	}
}
const mapStateTodispatch = (dispatch) => {
	return {
	
	}
}
export default connect(mapStateToProps, mapStateTodispatch)(Writer);
复制代码

     react-redux抛出了一个connect方法 用来将react组件和store作绑定连接,connect方法接受两个参数,一个是mapStateToProps,这个参数的做用是根据组件自身的状态选择自身须要的数据,由于这个方法它能够访问store里面全部的元数据,一旦使用错误 会形成性能上的损耗,因此要谨慎使用,另外一个是mapStateTodispatch,包含一个dispatch方法,负责将视图的行为也就是action传递给reducer进行处理,最后跟上组件名称则表示组件已经和redux进行了关联,为何用这种方式就能关联,先跳过,咱们稍后再详细说明。

4.定义action 触发

     到这里须要明确两件事: 1.我要拿到什么。 2经过什么方式拿 dispatch派发的action通常状况下是一个对象,可是当涉及到异步时 action也能够是一个函数,典型的例子就是上面这个writerList咱们须要从服务端取得,这个时候咱们可能须要定义一个函数类型的action代码以下:

import { actionCreators } from '../store/index'
const mapStateToProps = (state) =>{
  return {
        list: state.getIn(['home', 'writerList'])
	}
}
const mapStateTodispatch = (dispatch) => {
	return {
		getWriterList(){
			dispatch(actionCreators.getWriterList())
		}
	}
}
componentDidMount(){
	this.props.getWriterList()
}
复制代码

     list就是咱们最终要取得的数据,咱们须要经过getWriterList() 这个方法去获取数据,由于已经作了关联, 咱们能够在生命周期钩子里去调用这个函数 此时dispatch触发的是一个异步函数 actionCreators.js文件中的getWriterList()方法 为了方便管理 能够都统一拆分到一个actionCreators.js文件之中而后抛出 其代码以下:

actionCreators.js内容以下
export const getWriterList = () => {
// 借用redux-chunk可使action处理异步  标记是返回的是不是函数
   return (dispatch) => {
      axios.get('/api/writerList.json').then(res=>{
				const result  = res.data.data;
				dispatch(addWriterList(result))
			}).catch(err=>{
				console.log(err)
			})
	 }
}
复制代码

     很明显在这里,咱们发出了一个请求, 将返回的结果再次dispatch触发 此时将结果传入到一个addWriterList()方法中,代码以下:

export const ADD_WRITER_LIST = 'home/ADD_WRITER_LIST'; constants.js

actionCreators.js内容添加方法
import * as constants from './constants';
const addWriterList = (result) => {
	return {
		type: constants.ADD_WRITER_LIST,
		writerList: fromJS(result.writerList)
	}
}
复制代码

     这个方法最后return出一个对象 符合了非异步状况下action通常是一个object的规则 是否是好奇这个constants,其实就是定义的actionType,reducer须要根据这个type来作出相应处理。

5.reducer过滤

reducer是一个纯函数,本质上传入必定类型的数据,必然返回必定类型的结果,作具体的数据过滤尤为合适,

reducer.js内容以下
const defaultState = fromJS({
	writerList: [],
});
 const addWriterList = (state, action)=>{
  return state.set('writerList', action.writerList)
}
 export const ADD_WRITER_LIST = 'home/ADD_WRITER_LIST'; constants.js
 export default (state = defaultState, action) => {
	switch(action.type) {
		case constants.ADD_WRITER_LIST:
			return addWriterList(state, action);
		default:
			return state;
	}
}
复制代码

     reducer接受两个参数 第一个是state元数据, 第二个是action视图触发的动做类型,经过这两个信息 reducer就能够返回符合指望的信息,参照全部图书和三国演义的例子。根据对应的ADD_WRITER_LIST类型 执行完addWriterList方法后 此时 defaultState 的 writerList, 已是一个通过服务端数据填充的元数据,而由于组件与redux作了连接 因此页面会正确的渲染出目标视图,即简书网站的列表组件。

怎么作的连接?

你们可能一直有疑问为何经过connect方法就能和组件作连接, connect背后的逻辑是这样的

1.建立一个全局store对象

import { createStore, compose, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import reducer from './reducer';
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(reducer, composeEnhancers(
	applyMiddleware(thunk)
));
export default store;
复制代码

     相似上面的代码你们确定都很熟悉,能够先无论引入的reducer,只须要知道是处理具体的过滤逻辑便可,是一个庞大的模块,从上面的redux模块中 引入了createStore这个方法并执行, applyMiddleware是做为中间件处理异步,thunk可使传入的action不只仅是对象, 还能够是函数,执行createStore()方法后把它抛出。 那咱们再来看reducer,reducer是一个庞大的模块,那么必然是有一个个小的模块去组成的,每个小的模块,又包含 redux的各类角色。这种拆分方式有利于各个模块之间的数据管理不被污染

假如将咱们刚才作的WriterWrapper组件看做一个小的store 那么它的结构如今是这样的

index.js内容以下
import reducer from './reducer';
import * as actionCreators from './actionCreators';
import * as constants from './constants';
export { reducer, actionCreators, constants };
这个文件引入了 刚才所须要的全部内容 并作了抛出  咱们姑且把它放到home目录下。
复制代码

2 .建立一个全局reducer文件

import { combineReducers } from 'redux-immutable';
import { reducer as homeReducer } from '../pages/home/store';
const reducer = combineReducers({
	home: homeReducer,
})
export default reducer;
复制代码

这个文件引入了刚才抛出的那个home目录下的reducer对象,介绍一下combineReducers是为了作多个reducer的合并

3.绑定视图

import React, { Component } from 'react';
import { Provider } from 'react-redux';
import { BrowserRouter, Route } from 'react-router-dom';
import Header from './common/header';
import Home from './pages/home';
import store from './store';

class App extends Component {
  render() {
    return (
    	<Provider store={store}>
      	<BrowserRouter>
      		<div>
            <Header />
      			<Route path='/' exact component={Home}></Route>
      		</div>
      	</BrowserRouter>
      </Provider>
    );
  }
}
export default App;
复制代码

     经过react-redux的provider提供商 将整个容器包裹,因而就至关于整个store和应用作了连接,这时就有了组件的connect连接,这样一个完整的redux流程就基本造成了。

写在最后

    一直纠结于文章的顺序安排,有些地方顺序安排的不合理,ps:(千万别误人子弟了)你们也能够给出本身的看法,我会继续补充修改本文。

文章参考:

www.jdon.com/idea/flux.h…

cn.redux.js.org/

yq.aliyun.com/articles/66…

相关文章
相关标签/搜索