权当是一个自我总结, 这个星期搭建Redux的框架.
他的核心概念, 我不会刻意说, 在他的官网上面, 一开始就说了, 而且光是说出来, 其实感受并不深刻.
就想粗略的说一下自己在搭建过程中的步骤, 以及对所不明白的地方学习.
对于整体Redux的数据流方向
Action (Dispatch) -> Reducer -> Store -> Component(who subscribe)
用白话描述的话, 就是组件里面UI交互, 出发了Action, 这个Action通过Store的Dispatch方法将信息传递到了Reducer里面, Reducer根据Action传递来的信息, 进行数据的状态的配对以及改写(不准确, 其实是对赋值值的改写), 然后因为有改变, 在组件里面的订阅方法(Subscriber)被调用, 获得了改变以后的状态值, 然后用这些数据去对应UI交互所带来的组件变化.
创建Action
import * as types from '../constants/ActionTypes';
export function connectReadyClick(currentState) {
var currentImage = currentState ? './img/bpm1SetwifiReady@2x.png':'./img/bpm1SetwifiUnready@2x.png';
return{type:types.CONNECT_CLICK_READY,currentState,currentImage
}
}
POINT:
- 对于Action的Type, 要有对应统一管理的文件, 在上面代码的例子里面, 这个文件是ActionTypes.
//PrepareConnect Action types
export const CONNECT_CLICK_READY = 'CONNECT_CLICK_READY';
- 将业务代码写在这里面. 这个在上一篇文章里面已经说过了.
创建对应的Reducer
import * as types from '../constants/ActionTypes';
//这个是一开始的时候的状态, 这个是由我们来定制的.
//现在想的话应该是每次自己写
const initialState = {
readyButtonState:false, //初始化的状态时候是没有被点下的
appearImage:'./img/bpm1SetwifiUnready@2x.png'
}
export default function prepareConnect(state = initialState,action) {
switch (action.type){
case types.CONNECT_CLICK_READY: {
return Object.assign({},state,{
readyButtonState:action.currentState,
appearImage:action.currentImage,
})
}
break;
default:
return state;
break;
}
}
initialState, 是作为默认状态, 或者说是初始化状态存在的, 当当前的Reducer所负责的Action没有被触发的时候, 他所负责的状态树的数据就是initialState这部分.
也就是说, 如果说我们的组件数据完全是Redux这个框架驱动的话, 这里所说的initialState里面的数据, 就是在没有任何Action被触发的情景下, 你的组件默认要去使用的数据.
POINT:
-
export default function prepareConnect(state = initialState,action)
替代了
if (typeof state === 'undefined') {
return initialState
}
这个事ES6参数默认值, 意思就是在没有传入参数的时候(undefined), 那么参数就取等号右边的.
- Object.assign()
在Redux的三大原则里面, 其中一条就是State是只读的.
所以在这里, 我们不是直接的去更改State, 而是用Object.assign()这个方法去创建一个副本去操作.
注意: 第一个参数必须为空.
合并Reducer
import { combineReducers } from 'redux';
import prepareConnect from './prepareConnect';
const rootReducer = combineReducers({
prepareConnect
});
export default rootReducer;
背景是, 我已经将Reducer模块化了, 各个的Reducer维护自己对应模块的State.
那么我们需要用到Redux里面的一个工具类叫combineReducers
, 作用就是将各个分支的Reducer所维护的那个State树进行合并.
POINT:
- 在
combineReducers
函数中各个Reducer所import出来的名字, 其实就是他们所对应负责那部分State的Key值. 也就是{prepareConnect:hisState}
生成并配置Store
import { AsyncStorage } from 'react-native';
import { applyMiddleware, createStore, compose } from 'redux';
import thunk from 'redux-thunk';
import createLogger from 'redux-logger';
import {persistStore, autoRehydrate} from 'redux-persist';
import reducers from '../reducers';
var isDebuggingInChrome = __DEV__ && !!window.navigator.userAgent;
var logger = createLogger({
predicate: (getState, action) => isDebuggingInChrome,
collapsed: true,
duration: true,
});
var middlewares = compose(applyMiddleware(thunk, logger), autoRehydrate());
export default function configureStore() {
const store = createStore(reducers, undefined, middlewares);
persistStore(store, {storage: AsyncStorage});
if (isDebuggingInChrome) {
window.store = store;
}
store.subscribe(() => {
let state = store.getState();
console.log(state);
});
return store;
}
其实, 核心代码只有一句:
createStore(reducers, undefined, middlewares);
由他, 来创建出了三个角色中的最后一个Store
其他的剩下的代码, 都是属于配置代码.
POINT:
- 关于
creatStore()
, 第一个参数是传入Reducer
,第二个参数是整个Store的默认State树, 是可选的. 第三个参数是中间件, 也是可选的.
传入Reducer
, 使得Store持有了整个的State树, 并且当Reducer的State被触发了的Action所更新的话, Store会得到通知, 并且去通知到订阅的部分.
屏幕快照 2017-07-15 下午2.34.27.png接收两个参数,分别是当前的 state 树和要处理的 action,返回新的 state 树。
传入初始化的状态, 首先, 不推荐.... 因为在Reducer里面就已经有了各个部分的初始化状态了. 官方是这么说的:
在同构应用中,你可以决定是否把服务端传来的 state 水合(hydrate)后传给它,或者从之前保存的用户会话中恢复一个传给它。如果你使用 combineReducers 创建 reducer,它必须是一个普通对象,与传入的 keys 保持同样的结构。否则,你可以自由传入任何 reducer 可理解的内容。
传入Enhancer
, 则是属于配置Store
Store enhancer 是一个组合 store creator 的高阶函数,返回一个新的强化过的 store creator。这与 middleware 相似,它也允许你通过复合函数改变 store 接口。
Container
import React, {Component} from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import {
StyleSheet,
View
} from 'react-native';
import Actions from '../actions';
import Upper from "../components/prepareConnect-view/Upper";
import Bottom from "../components/prepareConnect-view/Bottom";
class PrepareConnectView extends Component {
static props = {}
constructor(props) {
super(props);
this.state = {}
}
render() {
return (
<View>
<Upper/>
<Bottom {...this.props}/>
</View>
)
}
}
//将状态并入到属性里面
function mapStateToProps(state) {
return {
prepareConnect:state.prepareConnect,
};
}
//将发送信号并入到属性里面
//作用是调用的话直接就去
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(Actions, dispatch)
}
}
//就是这个Connect将这两个东西并入了组件的属性里面
export default connect(
mapStateToProps,
mapDispatchToProps
)(PrepareConnectView);
React bindings for Redux embrace the idea of separating presentational and container components
from Redux Doc
Container这个角色是传统的将逻辑与UI进行隔离的手段.
也就是 每个页面, 可以分为两个部分, 一个部分为Component, 他是负责UI的显示.
另一个部分是Container, 负责的是逻辑处理以及对应Redux的属性绑定等.
POINT:
-
function mapDispatchToProps(dispatch)
是将store.dispatch
这个动作, 加入到了当前组件的属性里面, 可以使之直接调用. -
function mapStateToProps(state)
是将store.subscribe
这个监听方法以及监听所有带来的状态回调加入到了当前组件的属性里面, 可以让组件拿到最新的状态. -
...this.props
是ES6带来的展开运算符. 在这里他的作用就是, 将当前的组件的所有属性传递给了子组件.
其实, 我在写自己业务模块的时候, 觉得没有什么必要去用Redux, 他的这一套对我只是跳转和简单逻辑的模块来说太重了.
并且奉劝各位如果要用Redux的同学, Redux大量的重复代码比如Action
,以及Reducer
里面的, 应该不是自己手写出来的(当然, creatAction里面的逻辑代码什么的要自己写), 我不知道能不能理解我的意思.
中间件什么的就不说了.
整个流程里面还差一个Provider
中间件, 它使得所有的组件都可以访问到Store里面的属性.
昨天公司发生了一点事情.
让我心里很不舒服.
接下来应该是研究一下Mobx什么的.
网友评论