本文从将从 Redux 原理出发,一步步自己实现一个简单的 Redux,主要目的是了解 Redux 内部之间的联系。看本文之前先要知道 Redux 是怎么用的,对 Redux 用法不会讲解太多。
官网 redux demo
新建一个文件 redux.js,然后直接引入,观察控制台输出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
import { createStore } from 'redux' const defaultState = { value: 10 , } function reducer (state = defaultState, action ) { console .log(state, action) switch (action.type) { case 'INCREMENT' : return { ...state, value: state.value + 1 , } case 'DECREMENT' : return { ...state, value: state.value - 1 , } default : return state } } const store = createStore(reducer)const init = store.getState()console .log(`一开始数字为:${init.value} ` )function listener () { const current = store.getState() console .log(`当前数字为:${current.value} ` ) } store.subscribe(listener) store.dispatch({ type : 'INCREMENT' }) store.dispatch({ type : 'INCREMENT' }) store.dispatch({ type : 'DECREMENT' }) export default store
输出结果:
1 2 3 4 5 6 7 8
{value: 10} {type: "@@redux/INIT1.a.7.g.7.t"} 一开始数字为:10 {value: 10} {type: "INCREMENT"} 当前数字为:11 {value: 11} {type: "INCREMENT"} 当前数字为:12 {value: 12} {type: "DECREMENT"} 当前数字为:11
所有对数据的操作必须通过 dispatch 函数,它接受一个参数 action,action 是一个普通的 JavaScript 对象,action 必须包含一个
type
字段,告诉它要修改什么,只有它允许才能修改。
在每次调用进来 reducer 函数我们都打印了 state 和 action,我们手动通过 store.dispatch 方法派发了三次 action,但你会发现输出了四次.这是因为 Redux 内部初始化就自动执行了一次 dispatch 方法,可以看到第一次执行它的 type 对我们数据来说是没有影响的(因为 type 取值
@@redux/INIT1.a.7.g.7.t
,我们自己 redux 的数据 type 不会取名成这个样子,所以不会跟它重复),即默认输出 state 值
1 2 3
import { createStore } from 'redux' // 传入reducer const store = createStore(reducer)
1 2 3 4 5 6 7 8 9 10 11 12
export function createStore (reducer) { const getState = () => {} const subscribe = () => {} const dispatch = () => {} return { getState, subscribe, dispatch } }
1 2 3 4 5 6 7 8
export function createStore (reducer ) { let currentState = {} const getState = () => currentState return { getState, } }
1 2 3 4 5 6 7 8 9 10 11
export function createStore (reducer ) { let currentState = {} const getState = () => currentState const dispatch = action => { currentState = reducer(currentState, action) } return { getState, dispatch, } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
export function createStore (reducer ) { let currentState = {} let currentListeners = [] const getState = () => currentState const subscribe = listener => { currentListeners.push(listener) } const dispatch = action => { currentState = reducer(currentState, action) currentListeners.forEach(listener => listener()) } return { getState, subscribe, dispatch, } }
1 2 3 4 5
function listener () { const current = store.getState() console .log(`当前数字为:${current.value} ` ) } store.subscribe(listener)
1 2 3 4 5 6 7 8 9 10
constructor (props) { super (props) this .state = store.getState() this .storeChange = this .storeChange.bind(this ) store.subscribe(this .storeChange) } storeChange () { this .setState(store.getState()) }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
export function createStore (reducer ) { let currentState = {} let currentListeners = [] const getState = () => currentState const subscribe = listener => { currentListeners.push(listener) } const dispatch = action => { currentState = reducer(currentState, action) currentListeners.forEach(listener => listener()) } dispatch({ type : '@@mini-redux/~GSDG4%FDG#*&' }) return { getState, subscribe, dispatch, } }
1
import { createStore } from './mini-redux'
1 2 3 4 5 6 7 8
{} {type: "@@mini-redux/~GSDG4%FDG#*&"} 一开始数字为:undefined {} {type: "INCREMENT"} 当前数字为:NaN {type: "INCREMENT"} 当前数字为:NaN {value: NaN} {type: "DECREMENT"} 当前数字为:NaN
1 2 3 4 5 6 7 8 9 10
const defaultState = { value: 10 , } function reducer (state = defaultState, action ) { switch (action.type) { default : return state } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
export function createStore (reducer ) { let currentState let currentListeners = [] const getState = () => currentState const subscribe = listener => { currentListeners.push(listener) } const dispatch = action => { currentState = reducer(currentState, action) currentListeners.forEach(listener => listener()) } dispatch({ type : '@@mini-redux/~GSDG4%FDG#*&' }) return { getState, subscribe, dispatch, } }
1
createStore(reducer, [preloadedState], enhancer)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
export function createStore (reducer, preloadedState, enhancer ) { if (typeof preloadedState === 'function' && typeof enhancer === 'undefined' ) { enhancer = preloadedState preloadedState = undefined } if (typeof enhancer !== 'undefined' ) { if (typeof enhancer !== 'function' ) { throw new Error ('Expected the enhancer to be a function.' ) } return enhancer(createStore)(reducer, preloadedState) } if (typeof reducer !== 'function' ) { throw new Error ('Expected the reducer to be a function.' ) } let currentState = preloadedState let currentListeners = [] }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
export function createStore (reducer, preloadedState, enhancer ) { let currentListeners = [] const subscribe = listener => { if (typeof listener !== 'function' ) { throw new Error ('Expected listener to be a function.' ) } currentListeners.push(listener) return () => { currentListeners = currentListeners.filter(l => l !== listener) } } }
1 2 3 4 5 6 7 8 9 10 11
const subscribe = listener => { if (typeof listener !== 'function' ) { throw new Error ('Expected listener to be a function.' ) } currentListeners.push(listener) return () => { let index = currentListeners.indexOf(listener) currentListeners.splice(index, 1 ) } }
1 2 3
let unsubscribe = store.subscribe(() => console .log(store.getState()))unsubscribe()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
export function createStore (reducer, preloadedState, enhancer ) { let isDispatching = false const dispatch = action => { if (!isPlainObject(action)) { throw new Error ('Actions must be plain objects. ' ) } if (isDispatching) { throw new Error ('Reducers may not dispatch actions.' ) } try { isDispatching = true currentState = reducer(currentState, action) } finally { isDispatching = false } currentListeners.forEach(listener => listener()) return action } } export function isPlainObject (obj ) { if (typeof obj !== 'object' || obj === null ) return false let proto = obj while (Object .getPrototypeOf(proto) !== null ) { proto = Object .getPrototypeOf(proto) } return Object .getPrototypeOf(obj) === proto }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
export function createStore (reducer, preloadedState, enhancer ) { if (typeof preloadedState === 'function' && typeof enhancer === 'undefined' ) { enhancer = preloadedState preloadedState = undefined } if (typeof enhancer !== 'undefined' ) { if (typeof enhancer !== 'function' ) { throw new Error ('Expected the enhancer to be a function.' ) } return enhancer(createStore)(reducer, preloadedState) } if (typeof reducer !== 'function' ) { throw new Error ('Expected the reducer to be a function.' ) } let currentState = preloadedState let currentListeners = [] let isDispatching = false const getState = () => currentState const subscribe = listener => { if (typeof listener !== 'function' ) { throw new Error ('Expected listener to be a function.' ) } currentListeners.push(listener) return () => { currentListeners = currentListeners.filter(l => l !== listener) } } const dispatch = action => { if (!isPlainObject(action)) { throw new Error ('Actions must be plain objects. ' ) } if (isDispatching) { throw new Error ('Reducers may not dispatch actions.' ) } try { isDispatching = true currentState = reducer(currentState, action) } finally { isDispatching = false } currentListeners.forEach(listener => listener()) return action } dispatch({ type : '@@mini-redux/~GSDG4%FDG#*&' }) return { getState, subscribe, dispatch, } } export function isPlainObject (obj ) { if (typeof obj !== 'object' || obj === null ) return false let proto = obj while (Object .getPrototypeOf(proto) !== null ) { proto = Object .getPrototypeOf(proto) } return Object .getPrototypeOf(obj) === proto }
Redux 入门教程(一):基本用法