在两年前曾短暂的使用过react+redux进行开发,但是一入vue深似海,今后其余是路人,趁着闲的时间,赶忙撸一把redux在react的实现。下面来简单的和你们分享一下。html
在了解redux以前,有必要先了解一下flux, flux是Facebook用户创建客户端Web应用的前端架构,以单向数据流方式支持MVC, Flux应用有三个主要部分:Dispatcher调度,存储Store 和 视图View ,当一个用户和视图交互时,有一个分发器dispatcher用来发送动做action到数据存储中,而后更新视图,存储接受更新,适当地调节这些更新,而不是一致地依赖外部更新其数据,存储以外根本不知道它是如何管理领域数据的,这有助于实现一种清晰的分离关注。前端
这是flux的结构和数据流向图,不难看出一个单向数据流是Flux模式的核心。vue
你们若是稍有了解或者曾经接触的话,必定会听过这样一句话:若是说flux是一种思想,那么redux就是其中一种实现。 固然还有其它的实现方式如MoBx等。react
Redux是对flux的一种演变,也是flux思想的一种子集。设计目的在于 Redux 试图让 state 的变化变得可预测,Redux使Javascript的状态管理变得更加可预期,以一种新方式思考开发应用,这个方式是:状态从一个初始状态开始,被一系列动做序列改变,这种新方式是通往复杂Web应用的捷径。ios
redux的工做流程与其设计三大基本原则密不可分json
首先要明确一下在react中完整的redux流程须要那几个对象 或者角色。redux
再来看它的工做原理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秒,老易径自拿出一本三国演义给了小王,小王大喜连声道谢。因而捧书而走。 埋头苦读,遂成三国通。
整个场景与上面的阐述你们能够思考一下,上面主要是讲一下概念,你们最关心的确定是代码实现 下面分步骤来解释.
这是定义的上面那个列表的组件,那么组件写好以后,咱们就开始往里头填数据,数据从何而来,天然是redux。
reducer.js 内容以下:
import { fromJS } from 'immutable';
import * as constants from './constants';
const defaultState = fromJS({
writerList: [],
});
复制代码
这个writerList就是咱们最后要渲染到WriterWrapper里面的数据,接下来的一切行为都是为了取到这个writerList
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进行了关联,为何用这种方式就能关联,先跳过,咱们稍后再详细说明。
到这里须要明确两件事: 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来作出相应处理。
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:(千万别误人子弟了)你们也能够给出本身的看法,我会继续补充修改本文。
文章参考: