redux 基本api 的使用
-
store 保存数据的地方,整个应用只能有一个,利用createStore这个函数来生成
import { createStore } from 'redux'; const store = createStore(fn); -
state 对象包含所有数据,想要获得某个时间点的数据,可以通过 store.getState() 获取
import { createStore } from 'redux'; const store = createStore(fn); const state = store.getState(); -
Action 在view层面改变后 需要state也发生变化,这时候需要用到action
const action = { type: 'ADD_TODO', payload: 'Learn Redux' };type 属性是必须的 其他的可以自定义
-
store.dispatch()是view 发出action 的唯一方法
mport { createStore } from 'redux'; const store = createStore(fn); store.dispatch({ type: 'ADD_TODO', payload: 'Learn Redux' });dispatch 接收的参数是一个 action 对象
-
reducer 是一个函数,接收action 和 当前 state 作为参数,返回一个新的state
onst defaultState = 0; const reducer = (state = defaultState, action) => { switch (action.type) { case 'ADD': return state + action.payload; default: return state; } }; const state = reducer(1, { type: 'ADD', payload: 2 });reducer 是个纯函数,同样的输入,必答是同样的输出,遵守以下一些约束
- 不得改写参数
- 不能调用系统I/O的API
- 不能调用 Date.now() 或者 Math.random() 不纯的方法
-
store.subscribe() 使用这个方法可以设置监听函数,state发生改变,自动执行这个函数。 改方法返回值是一个函数,调用他可以解除监听
let unsubscribe = store.subscribe(() => console.log(store.getState()) ); unsubscribe();reducer 的拆分
reducer 数量大的时候,可以进行拆分。Redux 提供了一个
combineReducers方法,用于 Reducer 的拆分。你只要定义各个子 Reducer 函数,然后用这个方法,将它们合成一个大的 Reducer。mport { combineReducers } from 'redux'; const chatReducer = combineReducers({ chatLog, statusMessage, userName }) export default todoApp;
redux 源码阅读
-
首先从 createStore 开始说
export default function createStore< S, A extends Action, Ext = {}, StateExt = never >( reducer: Reducer<S, A>, preloadedState?: PreloadedState<S> | StoreEnhancer<Ext, StateExt>, enhancer?: StoreEnhancer<Ext, StateExt> ): Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext { // ... // reducer 是接收的参数 函数类型 let currentReducer = reducer let currentState = preloadedState as S let currentListeners: (() => void)[] | null = [] let nextListeners = currentListeners let isDispatching = false /** * Reads the state tree managed by the store. * * @returns The current state tree of your application. */ function getState(): S { // 获取 最新的 state return currentState as S } function subscribe(listener: () => void) { let isSubscribed = true ensureCanMutateNextListeners() // 添加监听函数到队列 nextListeners.push(listener) // 返回取消监听 的函数 return function unsubscribe() { isSubscribed = false ensureCanMutateNextListeners() // 从队列中找到监听函数 去掉 const index = nextListeners.indexOf(listener) nextListeners.splice(index, 1) currentListeners = null } } function dispatch(action: A) { try { isDispatching = true // 调用reducer 函数 获取最新的 state 值 currentState = currentReducer(currentState, action) } finally { isDispatching = false } const listeners = (currentListeners = nextListeners) // 对监听函数 遍历执行 for (let i = 0; i < listeners.length; i++) { const listener = listeners[i] listener() } return action } // When a store is created, an "INIT" action is dispatched so that every // reducer returns their initial state. This effectively populates // the initial state tree. dispatch({ type: ActionTypes.INIT } as A) const store = { dispatch: dispatch as Dispatch<A>, subscribe, getState, replaceReducer, [$$observable]: observable } as unknown as Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext return store }createStore 函数中定义了 dispatch getState ,subscribe,等函数,然后返回了一个对象,这个对象中引用了 函数体中的定义的属性,形成了一个闭包的结构体。
createStore 函数在调用的时候 会自己执行一次 dispatch dispatch({ type: ActionTypes.INIT } as A) 这样就把最初的state 值存放到了 作用域中。
-
返回的 store对象中 getState的处理 store.getState();
function getState(): S { // 获取 最新的 state return currentState as S }从上面的源码中可以看到,getState 返回值就是 闭包函数体中的 currentState 属性值,也就是最新的state
-
上面说到了 dispatch 是发出 action 来改变 state 的途径
function dispatch(action: A) { try { isDispatching = true // 调用reducer 函数 获取最新的 state 值 currentState = currentReducer(currentState, action) } finally { isDispatching = false } const listeners = (currentListeners = nextListeners) // 对监听函数 遍历执行 for (let i = 0; i < listeners.length; i++) { const listener = listeners[i] listener() } return action }从上面代码中我们可以看到 dispatch 接收一个action 参数,在执行中会调用 闭包函数初始化传入的reducer 函数, 并传入当前state 即 currentState,和action.在我们定义的reducer 函数中根据action 中的 type 属性来区分不同的操作,并返回新的 state
-
subscribe 来设置监听函数,并返回取消监听的函数
function subscribe(listener: () => void) { let isSubscribed = true ensureCanMutateNextListeners() // 添加监听函数到队列 nextListeners.push(listener) // 返回取消监听 的函数 return function unsubscribe() { isSubscribed = false ensureCanMutateNextListeners() // 从队列中找到监听函数 去掉 const index = nextListeners.indexOf(listener) nextListeners.splice(index, 1) currentListeners = null } }从上面代码中我们可以看到,每次调用subscribe 会将传入的监听函数存放在 队列当中, 并且返回一个函数,这个函数执行来取消这个监听函数,就是将回调函数从队列中去掉。 设置的监听函数会在dispatch 函数中被触发
-
对reducer 函数的拆分,提供了 combineReducers来实现对多个 reducer 函数的合并
export default function combineReducers(reducers: ReducersMapObject) { // 函数接收一个对象,对象中的键值对中的value 是要执行的 reducer 函数,将类型符合的放到了 finalReducers 新对象中 const reducerKeys = Object.keys(reducers) const finalReducers: ReducersMapObject = {} for (let i = 0; i < reducerKeys.length; i++) { const key = reducerKeys[i] if (process.env.NODE_ENV !== 'production') { if (typeof reducers[key] === 'undefined') { warning(`No reducer provided for key "${key}"`) } } if (typeof reducers[key] === 'function') { finalReducers[key] = reducers[key] } } const finalReducerKeys = Object.keys(finalReducers) // This is used to make sure we don't warn about the same // keys multiple times. let unexpectedKeyCache: { [key: string]: true } if (process.env.NODE_ENV !== 'production') { unexpectedKeyCache = {} } let shapeAssertionError: unknown try { assertReducerShape(finalReducers) } catch (e) { shapeAssertionError = e } // 返回一个函数,函数接收的参数是 state,和 action 也就是 createStore 接收的函数 return function combination( state: StateFromReducersMapObject<typeof reducers> = {}, action: AnyAction ) { if (shapeAssertionError) { throw shapeAssertionError } if (process.env.NODE_ENV !== 'production') { const warningMessage = getUnexpectedStateShapeWarningMessage( state, finalReducers, action, unexpectedKeyCache ) if (warningMessage) { warning(warningMessage) } } let hasChanged = false const nextState: StateFromReducersMapObject<typeof reducers> = {} // 当dispatch 调用reducer 的时候,在这里将闭包作用域中传入的reducer函数遍历执行 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) if (typeof nextStateForKey === 'undefined') { const actionType = action && action.type throw new Error( `When called with an action of type ${ actionType ? `"${String(actionType)}"` : '(unknown type)' }, the slice reducer for key "${key}" returned undefined. ` + `To ignore an action, you must explicitly return the previous state. ` + `If you want this reducer to hold no value, you can return null instead of undefined.` ) } nextState[key] = nextStateForKey hasChanged = hasChanged || nextStateForKey !== previousStateForKey } // 遍历过程会根据 传入reducer 对象的键值作为 key 形成一个state 对象,也就是每个单独的reducer 根据key 值存放自己的 state对象,可以防止各个state中重命名报错的情况 hasChanged = hasChanged || finalReducerKeys.length !== Object.keys(state).length return hasChanged ? nextState : state } }