引入
在使用 React 构建web应用的时候, 我们一般会通过 props 属性来进行父子间组件之间的通讯。但是当应用比较复杂,需要多个组件之间进行通讯时, 再使用 props 属性就不太合适了。此时, 我们可以使用 redux 来进行组件之间的通讯。
redux 关键对象及三大原则
在 redux 中, 有几个关键对象需要我们先简单了解一下:
-
State
web应用的 全局数据, 供应用中的多个组件共同使用。
-
Store
存储全局数据 State 的地方, 我们可以通过 store.getState() 方法获取全局对象 State。
-
action
本质上是一个普通的 js对象, 对象中包含我们要修改的数据。我们一般通过调用 store 对象的 dispatch() 方法,将修改后的数据传递到 store。
-
reducer
本质上是一个 纯函数,会返回一个 state 对象。当我们调用 store.dispatch(action) 时,就会执行 reducer函数,reducer函数 会接收旧的 state 和 action, 然后返回一个新的 state。
redux 的关键对象及其用法, 构成了 Redux 使用的三大原则:
-
单一数据源
一个 React应用 应且仅有一个 Store对象, Store对象 中包含着整个react应用的所有 全局数据State。
-
state是只读的
全局数据State 是只读的, 我们不能直接修改 State, 只能通过 store对象 的 dispatch 方法来触发 action,来修改 State。
-
使用纯函数来执行修改
Store
Store 是整个应用保存全局数据(State)的地方,每个应用应且只有一个 Store。 通过 Store, 我们可以获取、修改 State。
Redux 提供 createStore 方法, 来构建 Store 对象。
import {createStore} from 'redux';
// fn是一个函数,为 reducer, 会返回一个 state
let store = createStore(fn)
Store 对象提供了一系列 API 供用户使用:
-
getState()
返回当前应用的全局数据 State。
-
dispatch(action)
分发 action,改变应用的全局数据 state。 dispatch方法是触发 State 变化的唯一途径。
-
subscribe(listener)
添加一个变化监听器 - listener, 每当 dispatch(action) 的时候就会执行。
-
replaceReducer(nextReducer)
替换 store 当前用来计算 state 的 reducer。
action
由于 State 是只读的, 我们不能直接修改 State。通过 Action, 我们可以 表达想要修改 State 的意图, 通知 Redux 来修改 State。
Action 本质上一个 js对象 , 格式如下所示。其中, type 属性是必须的,是 Action 的名称,表示一类修改 State 的方式, 其他属性可以自由设置。
const action = {
type: 'ADD_TODO', // 将要执行的动作: ADD_TODO, 向任务列表中添加内容
text: '123', // 添加的内容的名称
...
}
在应用中, 每次修改 State, 我们都需要定义对应的 Action对象, 相同类型 Action对象, 我们可以通过 Action Creator函数 来定义。
const addTodo = text => {
type: 'ADD_TODO',
text
} // 我们通过执行addTodo('123'), 返回一个Action对象{type: 'ADD_TODO', text: '123'}
定义好 Action对象 之后, 我们必须通过 dispatch 方法, 发送 Action对象, 然后触发 State对象 的修改。
const store = Store.createStore(fn);
store.dispatch(addTodo('123'));
reducer
Action对象 仅仅表达了想要修改 State 的意图, 我们还需要定义一个对象来表达如何修改State对象, 这个对象就是 Reudecer 对象。
const todos = (state = [], action) {
switch(action.type) {
case 'ADD_TODO':
return [
...state, {
text: action.text
}
];
default:
return state;
}
}
Reducer, 本质上是一个 函数, 它接受 当前state对象 和 Action对象 作为参数,返回 新的state对象。
Redux API 解析
-
createStore(reducer, [prelodedState], enhancer)
-
功能
创建一个 Store对象 来存放应用中的所有 全局state, 一个应用中有且应有一个Store对象
-
参数
-
reducer | function
接收两个参数, 分别是 当前管理的State数据 和 Action对象, 返回一个新的 State 数据。
-
preloadedState | any
初始化时的 State数据。
-
enhancer | function
函数增强器,是一个函数, 返回一个新的 store create。
执行新的 store create 方法时, 会生成一个 store 对象, store 对象的 dispatch方法 会被函数增强器包装。
-
-
返回值
返回一个 store 对象, 保存应用中的所有 state 数据。 一般在使用第三方中间件时, 会添加 enhancer参数 来构建 store对象。
-
用法示例
import { createStore, applyMiddleware } from 'redux'; import thunk from 'redux-thunk' import { createLogger } from 'redux-logger' import reducers from './reducers/index' // redux 中间件 const middleware = [thunk, createLogger]; // 构建一个store对象 const store = createStore(reducers); // 利用reducers, store增强器, 构建一个store对象 const store = createStore(reducers, applyMiddleware(middleware));
-
源码解析
/** * 构建store对象 * @param reducer 用户提供的 reducer,用于返回更新以后的 state, 是一个纯函数 * @param preloadedState 构建 store 对象时传入的初始 state * @param enhancer 函数增强器, 应用中间件 * @return Store 返回一个构建的 store 兑现 **/ export default function createStore(reducer, preloadedState, enhancer) { // 对应 createStore(reducer, 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.') } // 如果传入了 enhancer, 则将旧的 store creater 传入 enhance, // 返回一个新的 store creater, 并传入 reducer、preloadedState执行 return enhancer(createStore)(reducer, preloadedState) } } ... // store 对象当前使用的 reducer var currentReducer = reducer; // store 对象对应的 state var currentState = preloadedState; // store 对象通过 subscribe 注册的 listeners var currentListeners = []; // 是否处于 dispatch 过程中 var isDispatching = false; ... // 获取当前应用的全局 state数据 function getState() { // reducer、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.'); } // 返回当前的 state return currentState; } // 注册一个 listener, 监听 state 数据的变化 // state 更新时触发 function subscribe(listener) { if (typeof listener !== 'function') { throw new Error('Expected the listener to be a function.'); } // reducer 执行期间不能注册 listener? if (isDispatching) { throw new Error('You may not call store.subscribe() while the reducer is executing. ' + 'If you would like to be notified after the store has been updated, subscribe from a ' + 'component and invoke store.getState() in the callback to access the latest state. ' + 'See https://redux.js.org/api-reference/store#subscribe(listener) for more details.'); } var isSubscribed = true; ensureCanMutateNextListeners(); nextListeners.push(listener); // 返回一个函数, 用于卸载注册的 listener return function unsubscribe() { if (!isSubscribed) { return; } if (isDispatching) { throw new Error('You may not unsubscribe from a store listener while the reducer is executing. ' + 'See https://redux.js.org/api-reference/store#subscribe(listener) for more details.'); } isSubscribed = false; ensureCanMutateNextListeners(); var index = nextListeners.indexOf(listener); // 卸载注册的 listener nextListeners.splice(index, 1); }; } // 分发action, 修改state function dispatch(action) { // action 必须是一个对象 if (!isPlainObject(action)) { throw new Error('Actions must be plain objects. ' + 'Use custom middleware for async actions.'); } // action 对象必须有 type 属性 if (typeof action.type === 'undefined') { throw new Error('Actions may not have an undefined "type" property. ' + 'Have you misspelled a constant?'); } // dispatch 过程中不能再次 dispatch if (isDispatching) { throw new Error('Reducers may not dispatch actions.'); } try { isDispatching = true; // 使用 reducer 计算新的 state // reducer 应该是一个纯函数, 不要试图直接修改 state、action, 而是用返回值替换 state currentState = currentReducer(currentState, action); } finally { isDispatching = false; } // 收集的 listener var listeners = currentListeners = nextListeners; // 更新 state 以后, 触发注册的 listener for (var i = 0; i < listeners.length; i++) { var listener = listeners[i]; listener(); } // 返回传入的 action 对象 return action; } // 替换 store 当前用来计算 state 的 reducer function replaceReducer(nextReducer) { // nextReducer 必须是一个函数 if (typeof nextReducer !== 'function') { throw new Error('Expected the nextReducer to be a function.'); } // 替换 reducer currentReducer = nextReducer; // 替换 reducer 以后, 更新 state dispatch({ type: ActionTypes.REPLACE }); } ... // 执行 reducer,初始化 state dispatch({ type: ActionTypes.INIT }); return { dispatch, subscribe, getState, replaceReducer, [$$observable]: observable }
-
-
applyMiddleware(...middleware)
-
功能
扩展 Redux, 使用 middleware 来 包装 store 对象的 dispatch 方法
-
参数
...middleware | arguments
每个 middleware 接受 Store 的 dispatch 和 getState 函数作为命名参数,并返回一个函数。
即 ({getState, dispatch}) => next => action
next => action, next 为上一个 middleware 返回的新的 dispatch 方法,action 为返回的一个新的接收 action 的 dispatch方法。
中间件middleware调用的顺序为从右到左。
-
返回值
返回一个应用了 middleware 的 store enhancer。
enhancer是一个函数,接收 store creater, 返回一个 新的 store creater。
即:createStore => createStore。
-
用法实例
import { createStore, applyMiddleware } from 'redux'; import thunk from 'redux-thunk' import { createLogger } from 'redux-logger' import reducers from './reducers/index' // redux 中间件 const middleware = [thunk, createLogger]; // 利用reducers, store增强器, 构建一个store对象 const store = createStore(reducers, applyMiddleware(middleware));
-
源码解析
// applyMiddleware接收一系列middleware, 然后返回一个enhancer export default function applyMiddleware(...middlewares) { // 返回enhancer, createStore => createStore return (createStore) => (reducer, preloadedState, enhancer) => { // 执行 redux 原生的 createStore方法, 此时 enhancer 为 undefined const store = createStore(reducer, preloadedState, enhancer) // redux 原生的 dispatch 方法 let dispatch = store.dispatch // 中间件链 let chain = [] // redux 原生的 getState 和 dispatch const middlewareAPI = { getState: store.getState, dispatch: (action) => dispatch(action) } // [chainA, chainB, chainC, chainD], 格式为 next => action // 每个 chain 中都通过闭包保存 redux 原生的 store、 store.getState、store.dispatch chain = middlewares.map(middleware => middleware(middlewareAPI)) // 即 chainA(chainB(chainC(chainD(store.dispatch)))) // 使用中间件包装原生的 dispatch 方法 dispatch = compose(...chain)(store.dispatch) // 返回一个新的Store对象, dispatch方法被重新包装 return { ...store, dispatch } } }
-
-
compose(...func)
-
功能
从右到左 来 组合多个函数, 返回一个最终的函数。
即将 [funcA, funcB, funcC, funcD], 合成 funcA(funcB(func(funcD(params))))
-
参数
需要合成的多个函数。预计每个函数都接收一个参数。它的返回值将作为一个参数提供给它左边的函数,以此类推。例外是 最右边的参数可以接受多个参数。
-
返回
从右到左把接收到的函数合成的最终函数。
-
用法实例
import { createStore, applyMiddleware, compose } from 'redux' import thunk from 'redux-thunk' import { createLogger } from 'redux-logger' import reducer from '../reducers/index' const store = createStore( reducer, // thunk(createLogger(createStore)), 返回一个新的createStore compose( applyMiddleware(thunk), applyMiddleware(createLogger) ) )
-
源码解析
export default function compose(...funcs) { if (funcs.length === 0) { return arg => arg } if (funcs.length === 1) { return funcs[0] } return funcs.reduce((a, b) => (...args) => a(b(...args))) }
-
-
combineReducers(reducers)
-
功能
将多个 reducer 合并为一个 combine reducer。调用 dispatch 派发 action 时,使用 combine reducer 来更新 state。
由 combineReducers() 返回的 state 对象,会将传入的每个 reducer 返回的 state 按其传递给 combineReducers() 时对应的 key 进行命名。
-
参数
-
reducers | object
参数 reducers 是一个普通对象,属性值为需要合并的 reducer。
-
-
返回值
一个调用 reducers 对象里所有 reducer 的 combine reducer。
使用 combine reducer 时,会将 reducers 中包含的所有 reducer 都触发一遍。
执行 combine reducer 会构造一个与 reducers 对象结构相同的 state 对象。
-
用法示例
具体用法详见: combineReducers。
-
源码解析
/** * 将多个 reducer 合并生成一个 combine reducer * 在执行 createStore 的时候, 会触发 combine reducer, 初始化 state * state 是一个对象, key 是 reducers 的 key, 对应的值为执行 reducer 以后的结果 * 使用 合并reducer 以后,只要执行 dispatch, 每一个 reducer 都要触发一次。 * @param {*} reducers 一个包含多个 reducer 的对象 */ function combineReducers(reducers) { // 所有 reducers 的 key var reducerKeys = Object.keys(reducers); // 合并以后的最终 reducer var finalReducers = {}; // 遍历 reducers, 收集满足条件的 reducer for (var i = 0; i < reducerKeys.length; i++) { var key = reducerKeys[i]; ... if (typeof reducers[key] === 'function') { // 要合并的 reducer 必须是一个函数 finalReducers[key] = reducers[key]; } } // 最终的 reducer 的 key var finalReducerKeys = Object.keys(finalReducers); var unexpectedKeyCache; { unexpectedKeyCache = {}; } var shapeAssertionError; try { // 校验每一个 reducer 的返回值是否是非 undefined assertReducerShape(finalReducers); } catch (e) { shapeAssertionError = e; } // 返回一个最后合并生成的的 combine reducer, 供 dispatch 触发 return function combination(state, action) { // 如果 state 是 undefined, 那么将 state 初始化为一个空对象 : {} // 调用 createStore 初始化 state 会出现这种情况 if (state === void 0) { state = {}; } ... var hasChanged = false; // 新的 state var nextState = {}; // 遍历要合并的所有 reducer for (var _i = 0; _i < finalReducerKeys.length; _i++) { // reducer 的 key var _key = finalReducerKeys[_i]; // 相应的 reducer var reducer = finalReducers[_key]; // 获取 reducer 对应的 state var previousStateForKey = state[_key]; // 更新以后的 state var nextStateForKey = reducer(previousStateForKey, action); if (typeof nextStateForKey === 'undefined') { var errorMessage = getUndefinedStateErrorMessage(_key, action); throw new Error(errorMessage); } // 构建 reducer 对应的 state nextState[_key] = nextStateForKey; // 判断 state 是否发生更新 hasChanged = hasChanged || nextStateForKey !== previousStateForKey; } return hasChanged ? nextState : state; }; }
-
-
bindActionCreators
-
功能
将 actionCreator 和 dispatch 合并,返回一个函数。执行这个函数时,先构建 action 对象, 然后通过 dispatch 方法派发 action, 触发 state 更新。
-
参数
-
actionCreators | object | function
参数 actionCreators 可以是一个 function, 用于构建一个 action。 也可以是一个普通对象, 对象中的每一个属性值都是一个 actionCreator。
-
dispatch | function
派发 action, 触发 state 更新。
-
-
返回值
bindActionCreators 方法的返回值是 一个函数 或者 对象。
如果传入的 actionCreators 是一个函数, 那么返回值也是一个函数,这个函数合并 actionCreator 和 dispatch,执行时会先通过 actionCreator 构建 action 对象,然后通过 dispatch 派发 action 触发 state 更新。
如果传入的 actionCreators 是一个对象, 那么返回值也是一个对象。对象的每一个属性值都是合并 actionCreator 和 dispatch 以后生成的方法。
-
用法示例
具体用法详见官网 bindActionCreators。
-
源码解析
/** * 将 dispatch 方法和 actionCreator 方法合并一个方法 * 执行这个方法时先创建 action, 再调用 diapatch 方法 * @param {*} actionCreator actionCreator * @param {*} dispatch 派发 action, 触发 state 的更新 */ function bindActionCreator(actionCreator, dispatch) { return function () { return dispatch(actionCreator.apply(this, arguments)); }; } /** * 将多个 actionCreators 和 dispatch合并 * @param {*} actionCreators 一个普通对象, 包含多个 actionCreators * @param {*} dispatch 派发 action, 触发 state 的更新 */ function bindActionCreators(actionCreators, dispatch) { // 如果 actionCreators 是一个函数, 直接调用 bindActionCreator 返回合并以后的方法 if (typeof actionCreators === 'function') { return bindActionCreator(actionCreators, dispatch); } // 如果 actionCreators 不是对象或者为空, 抛出异常 if (typeof actionCreators !== 'object' || actionCreators === null) { throw new Error("bindActionCreators expected an object or a function, instead received " + (actionCreators === null ? 'null' : typeof actionCreators) + ". " + "Did you write \"import ActionCreators from\" instead of \"import * as ActionCreators from\"?"); } // 合并以后的方法集合 var boundActionCreators = {}; // 遍历 actionCreators 中的 actionCreator,通过 bindActionCreator 生成合并方法 for (var key in actionCreators) { var actionCreator = actionCreators[key]; if (typeof actionCreator === 'function') { boundActionCreators[key] = bindActionCreator(actionCreator, dispatch); } } // 返回合并以后的方法集合 return boundActionCreators; }
-