import './index.css'
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App/App'
import { createStore } from 'redux'
const store = createStore((state = { count: 1 }, action) => {
switch (action.type) {
case 'ADD':
state.count += action.value
break
case 'MINUS':
state.count -= action.value
break
}
return state
})
function createAction(type, value) {
return () => {
store.dispatch({
type,
value
})
}
}
function render() {
ReactDOM.render(
<App {...store.getState()} increase={createAction('ADD', 1)} decrease={createAction('MINUS', 1)} />,
document.getElementById('root')
)
}
render()
store.subscribe(render)
以上是使用Redux的一个最简单的例子。涵盖了redux的基本使用方式,用户点击按钮 --> dispatch(action) --> 更新store --> 触发监听函数render,重绘。
文件目录
- applyMiddleware.js // Redux的插件机制实现,可以重点学习一下
- bindActionCreators.js // 将ActionCreator和dispatch绑定的工具
- combineReducers.js // 将store分层的工具
- compose.js // 将数个函数合并嵌套执行
- createStore.js // 核心,生成store
- index.js // 入口
combineReducers
const store = createStore(combineReducers({PageA: PageAReducer, PageB: PageBReducer}))
因为createStore的接收的是一个函数,当dispatch发生时,它就会被执行,将当前的store和被触发的action作为参数,传入这个函数。所以combineReducers的主要任务就是:遍历一个combinedReducers对象,将每个子reducer依次执行,并将执行结果赋予其所对应的key,最后返回一个总的newStore。有了以上的基础,我们来看源码实现:
export default function combineReducers(reducers) {
const reducerKeys = Object.keys(reducers)
const finalReducers = {}
for (let i = 0; i < reducerKeys.length; i++) {
const key = reducerKeys[i]
// 将每一个子reducer,保存到finalReducer对象中
if (typeof reducers[key] === 'function') {
finalReducers[key] = reducers[key]
}
}
const finalReducerKeys = Object.keys(finalReducers)
return function combination(state = {}, action) {
let hasChanged = false
const nextState = {}
for (let i = 0; i < finalReducerKeys.length; i++) {
const key = finalReducerKeys[i]
// 取出一个子reducer
const reducer = finalReducers[key]
// 取得这个key对应的旧的值
const previousStateForKey = state[key]
// 核心操作,将旧的state和action传入这个reducer,得到新的state
const nextStateForKey = reducer(previousStateForKey, action)
if (typeof nextStateForKey === 'undefined') {
const errorMessage = getUndefinedStateErrorMessage(key, action)
throw new Error(errorMessage)
}
nextState[key] = nextStateForKey
hasChanged = hasChanged || nextStateForKey !== previousStateForKey
}
return hasChanged ? nextState : state
}
}
applyMiddleware
createStore(reducers, applyMiddleware(reduxLogger, reduxPromise, reduxThunk))
这个插件机制是Redux一个很重要的功能,使得我们可以很方便的对action做各种各样的操作。比如日志打印,异步数据获取等等,只要添加一个middleware,就可以实现。这个类似于Express或者Koa中的中间件机制。我们来看一下他们是如何实现的
export default function applyMiddleware(...middlewares) {
return createStore => (...args) => {
// 这里通过将createStore方法和参数传入的方式,得到store
// 为什么不直接将store传入呢?因为,我们查看createStore的代码,可以发现
// return enhancer(createStore)(reducer, preloadedState)
// 这里的 enhancer其实就是当前的applyMiddeware,通过这种方式,可以保证window.store是由applyMiddleware最后return出去的
// 这个对我们以后写组件有借鉴意义,比如
// 我们有一个 ComponentA: const compA = new Component(args);
// 现在我们想强化一下CompoentA的功能,通过传入组件B的方式,那么, const enhancedCompA = new ComponentA(args, B)
// 这时候,如果B需要ComponentA原有的结果,那么就可以使用这种方式,最后返回的结果一定是经过B处理过的
const store = createStore(...args)
let dispatch = () => {}
let chain = []
const middlewareAPI = {
getState: store.getState,
dispatch: (...args) => dispatch(...args)
}
// 将getState和dispatch,作为参数传给每一个middleware
chain = middlewares.map(middleware => middleware(middlewareAPI))
/**
这个compose的作用就是: compose(a,b,c) ===> (...args) => a(b(c(...args)))
假设有[a,b,c]三个middleware,他们都长这样:
({ dispatch, getState }) => next => action => {
// 对action的操作 blablabla
return next(action);
};
那么,c会最先接收到一个参数,就是store.dispatch,作为它的next。然后c使用闭包将这个next存起来,把自己作为下一个middleware b的next参数传入。这样,就将所有的middleware串起来了。
最后,如果用户dispatch一个action,那么执行顺序会是: c --> b --> a
**/
dispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch
}
}
}
createStore
createStore主要会返回三个东西:
- getState
- subscribe
- dispatch
getState
很简单,返回当前的store
function getState() {
// 这里有个锁的机制,保证当另一个action在dispatch时,getState无法生效
if (isDispatching) {
throw new Error(
'You may not call store.getState() while the reducer is executing. ' +
'The reducer has already received the state as an argument. ' +
'Pass it down from the top reducer instead of reading it from the store.'
)
}
return currentState
}
subscribe
注册监听函数
function subscribe(listener) {
if (isDispatching) {
throw new Error(...)
}
let isSubscribed = true
// 这里Redux维护了两个Listener List:
// currentListeners
// nextListeners
// 因为currentListeners和nextListeners是独立的,就可以保证当currentListeners还在执行的时候,调用subscribe或unsubscribe是不会影响到原先原先注册的listeners的。
// 只有当下一次dispatch前,nextListeners又被同步给了currentListeners,之前的注册注销才会生效
ensureCanMutateNextListeners()
nextListeners.push(listener)
return function unsubscribe() {
if (!isSubscribed) {
return
}
if (isDispatching) {
throw new Error(...)
}
isSubscribed = false
ensureCanMutateNextListeners()
const index = nextListeners.indexOf(listener)
nextListeners.splice(index, 1)
}
}
dispatch
redux中最常用的一个方法了
function dispatch(action) {
if (isDispatching) {
throw new Error('Reducers may not dispatch actions.');
}
try {
isDispatching = true;
// 使用绑定的reducer,计算出新的store
currentState = currentReducer(currentState, action);
} finally {
isDispatching = false;
}
// 触发所有绑定的监听器
var listeners = currentListeners = nextListeners;
for (var i = 0; i < listeners.length; i++) {
var listener = listeners[i];
listener();
}
return action;
}