注:本文适合有使用过Redux或者有了解过的童鞋阅读~
接触过的项目很多都用到了Redux,在今天之前,我只是会使用这个工具来进行数据的存储与处理,觉得这真是个好东西,毕竟在一个大的项目中,单单靠props、state、ref来传递数据还是不够的。
面试的时候也被问到了Redux的原理,但是只答了它的用法,没答上它的原理。学习不仅做到知其然,还要做到知其所以然。今天抽了个时间去了解了一下,其实也就那么回事吧~哈哈哈
保证数据的单向流动,同时便于控制、使用、测试。
action
只是描述要发生的事件,并不改变state
reducer
根据action的描述,改变state
dispatch
将要发生的事件,分发到reducer,触发改变。store.dispatch(action)
store
用来保存state;
提供 getState() 方法获取 state;
提供 dispatch(action) 方法更新 state;
通过 subscribe(listener) 注册监听器;
通过 subscribe(listener) 返回的函数注销监听器。
var createStore = require('redux').createStore;
var store = createStore(count);//count是个reducer
先看看createStore都返回了什么?
export default function createStore(reducer, initialState) {
...
return {
dispatch,
subscribe,
getState,
replaceReducer
}
}
可以看到,createStore接受两个参数:改变state的reducer和初始state
每个属性的含义是:
createStore关键代码解析
export default function createStore(reducer, initialState) {
var currentReducer = reducer
var currentState = initialState
var listeners = []
var isDispatching = false;
// 返回当前的state
function getState() {
return currentState
}
// 注册listener,同时返回一个取消事件注册的方法
function subscribe(listener) {
listeners.push(listener)
var isSubscribed = true
return function unsubscribe() {
if (!isSubscribed) {
return
}
isSubscribed = false
var index = listeners.indexOf(listener)
listeners.splice(index, 1)
}
}
// 通过action该改变state,然后执行subscribe注册的方法
function dispatch(action) {
try {
isDispatching = true
currentState = currentReducer(currentState, action)
} finally {
isDispatching = false
}
listeners.slice().forEach(listener => listener())
return action
}
// 替换reducer,修改state变化的逻辑
function replaceReducer(nextReducer) {
currentReducer = nextReducer
dispatch({ type: ActionTypes.INIT })
}
// 初始化时,执行内部一个dispatch,得到初始state
dispatch({ type: ActionTypes.INIT })
}
为每个reducer创建一个store,后期维护比较麻烦,通过combineReducers将多个reducer合并成一个rootReducer:
用法:
var combineReducers = require('redux').combineReducers;
var rootReducer = combineReducers({
count: count,
year: year,
});
// 创建store
var createStore = require('redux').createStore;
var store = createStore(rootReducer);
combineReducers源码分析:
export default function combineReducers(reducers) {
const reducerKeys = Object.keys(reducers)
const finalReducers = {}
//将合法的reducer提出来
for (let i = 0; i < reducerKeys.length; i++) {
const key = reducerKeys[i]
if (typeof reducers[key] === 'function') {
finalReducers[key] = reducers[key]
}
}
const finalReducerKeys = Object.keys(finalReducers)
//返回一个reducer,同样接受两个参数,state和action
return function combination(state = {}, action) {
let hasChanged = false
const nextState = {}
for (let i = 0; i < finalReducerKeys.length; i++) {
const key = finalReducerKeys[i]
const reducer = finalReducers[key]
const previousStateForKey = state[key]
const nextStateForKey = reducer(previousStateForKey, action)
nextState[key] = nextStateForKey
hasChanged = hasChanged || nextStateForKey !== previousStateForKey
}
return hasChanged ? nextState : state
}
}
利用react-redux工具实现二者的结合
用法:
var Provider = require('react-redux').Provider;
// App 为上层的Component
class App extend React.Component{
render() {
return (
);
}
}
var connect = require('react-redux').connect;
var actionCreators = require('...');
// MyComponent是与redux无关的组件
var MyComponent = require('...');
function select(state) {
return {
count: state.count
}
}
export default connect(select, actionCreators)(MyComponent)
React通过Context属性,可以将属性(props)直接给子孙component,无须通过props层层传递, Provider仅仅起到获得store,然后将其传递给子孙元素而已:
export default class Provider extends Component {
getChildContext() { // getChildContext: 将store传递给子孙component
return { store: this.store }
}
constructor(props, context) {
super(props, context)
this.store = props.store
}
componentWillReceiveProps(nextProps) {
const { store } = this
const { store: nextStore } = nextProps
if (store !== nextStore) {
warnAboutReceivingStore()
}
}
render() {
let { children } = this.props
return Children.only(children)
}
}
connect函数接受四个参数,分别是:
connect函数返回一个生产Component的函数(wrapWithConnect),然后再将真正的Component作为参数传入,这样就生产出一个经过包裹的Connect组件,该组件通过this.context获取祖先Component的store,props包括stateProps、dispatchProps、parentProps,合并在一起得到nextState,作为props传给真正的Component
export default function connect(mapStateToProps, mapDispatchToProps, mergeProps, options = {}) {
return function wrapWithConnect(WrappedComponent) {
class Connect extends Component {
constructor(props, context) {
// 从祖先Component处获得store
this.store = props.store || context.store
this.stateProps = computeStateProps(this.store, props)
this.dispatchProps = computeDispatchProps(this.store, props)
this.state = { storeState: null }
// 对stateProps、dispatchProps、parentProps进行合并
this.updateState()
}
shouldComponentUpdate(nextProps, nextState) {
// 进行判断,当数据发生改变时,Component重新渲染
if (propsChanged || mapStateProducedChange || dispatchPropsChanged) {
this.updateState(nextProps)
return true
}
}
componentDidMount() {
// 改变Component的state
this.store.subscribe(() = {
this.setState({
storeState: this.store.getState()
})
})
}
render() {
// 生成包裹组件Connect
return (
this.nextState} />
)
}
}
Connect.contextTypes = {
store: storeShape
}
return Connect;
}
}