这是刚接触Redux时整理的一份笔记,现在我们使用mobx替代了Redux,对于Redux就没有继续深入下去。如果本文有什么错漏,欢迎大家在评论区交流、斧正。
Redux的api有5个:
- createStore
- combineReducers
- applyMiddleware
- bindActionCreators
- compose
createStore(reducer, preloadedState, enhancer);
该方法返回store对象用于存放state。原则上每个应用在应该只有一个store。
方法接受三个参数:
- reducer 是唯一必传的参数。
一个处理state的funtcion,方法应该总是返回一个新的更新后的state,而不是直接去修改原始的 state;
示例:
const reducer = (state = [], action) => {
let result = Object.assign({}, state);
switch (action.type) {
case 'add':
result = result.concat(action.payload);
break;
default:
break;
}
return result;
};
const store = createStore(reducer);
- preloadedState 可选,state的初始值。
使用combineReducers时,preloadedState应该是个object。
当与enhancer一起使用,需要在enhancer中处理preloadedState。
源码展示:
if (typeof enhancer !== 'undefined') {
if (typeof enhancer !== 'function') {
throw new Error('Expected the enhancer to be a function.');
}
return enhancer(createStore)(reducer, preloadedState);
}
一般建议在reducer中初始化reducer。通常情况下,通过 preloadedState 指定的 state 要优先于通过 reducer 指定 state。
const reducer = (state = [], action) => {
let result = state;
switch (action.type) {
case 'add':
result = [...result, ...action.payload];
break;
default:
break;
}
return result;
};
const enhancer = (...a) => {
return (createStore) => {
return (reducer, preloadedState) => {
const store = createStore(reducer, preloadedState);
const dispatch = store.dispatch;
const getState = store.getState;
store.dispatch = (action) => {
console.log('dispatch', action);
dispatch(action);
};
store.getState = () => {
console.log('getState');
return getState();
};
return store;
};
};
};
const store1 = createStore(reducer, {});
store1.getState(); // {}
const store2 = createStore(reducer, {}, enhancer());
store2.getState(); // {}
- enhancer:可选,一个function,通过复合函数改变 store 接口,与middleware写法相似。方法返回的是store。
使用方法
// 代码接preloadedState的示例
store2.dispatch({
type: 'add',
payload: [1]
});
// logger add
combineReducers(reducers);
把一个由多个不同 reducer 函数作为 value 的 object,合并成一个最终的 reducer 函数。由 combineReducers() 返回的 state 对象,会将传入的每个 reducer 返回的 state 按其传递给 combineReducers() 时对应的 key 进行命名。
Redux只能存在一个store,所以多个store需要通过combineReducers合并reducer。
源码展示:
function combineReducers(reducers) {
var reducerKeys = Object.keys(reducers);
var finalReducers = {};
for (var i = 0; i < reducerKeys.length; i++) {
var key = reducerKeys[i];
if (process.env.NODE_ENV !== 'production') {
if (typeof reducers[key] === 'undefined') {
(0, _warning2['default'])('No reducer provided for key "' + key + '"');
}
}
if (typeof reducers[key] === 'function') {
finalReducers[key] = reducers[key];
}
}
combineReducers 会剔除所有值为undefined 和 不为function类型的reducer,并试执行所有剩余reducer,验证是reducer是否有报错,否永远返回state。
但该错误不会立即提示,而是在将错误reducer传入createStore时才会提示
使用reducer时要避免出现同名action,多个同名action会同时生效。因为 combineReducer 生成的 reducer 会遍历所有 reducer,并返回 state.
var hasChanged = false;
var nextState = {};
for (var _i = 0; _i < finalReducerKeys.length; _i++) {
var _key = finalReducerKeys[_i];
var reducer = finalReducers[_key];
var previousStateForKey = state[_key];
var nextStateForKey = reducer(previousStateForKey, action);
if (typeof nextStateForKey === 'undefined') {
var errorMessage = getUndefinedStateErrorMessage(_key, action);
throw new Error(errorMessage);
}
nextState[_key] = nextStateForKey;
hasChanged = hasChanged || nextStateForKey !== previousStateForKey;
}
return hasChanged ? nextState : state;
};
使用示例:
const teacher = (state = [], action) => {
let result = state;
switch (action.type) {
case 'add':
result = [...result, ...action.payload];
break;
}
return result;
};
const student = (state = [], action) => {
let result = state;
switch (action.type) {
case 'add':
result = [...result, ...action.payload];
break;
}
return result;
};
const reducer = combineReducers({ teacher, student });
const store = createStore(reducer, {});
store.getState();
/*
{
student: [],
teacher: []
}
*/
store2.dispatch({
type: 'add',
payload: [1,2]
});
/*
{
student: [1, 2],
teacher: [1, 2]
}
*/
applyMiddleware(middlwares)
createStore时作为enhancer传入,相当于弱化版的enhancer,通过包装 dispatch 方法改变默认动作,它只能改变 dispatch。
中间件接收store的 dispatch 和 getState作为参数。
多个middleware调用顺序左 -> 右。
const teacher = (state = [1, 2, 3, 4, 5], action) => {
let result = [...state];
switch (action.type) {
case 'add':
result = [...result, ...action.payload];
break;
case 'shift':
result.shift();
break;
}
return result;
};
const middleWare1 = ({ dispatch, getState }) => { // store的api dispatch、getState、replaceReducer、subscribe
return (next) => {
return (action) => {
console.log('will dispatch', action);
// 调用 middleware 链中下一个 middleware 的 dispatch。
let returnValue = next(action); // 一般会是 action 本身,除非后面的 middleware 修改了它。
console.log('state after dispatch', getState());
return returnValue;
};
};
};
const middleWare2 = ({ dispatch, getState }) => {
return (next) => {
return (action) => {
action.type = 'shift';
console.log('will dispatch', action);
// 调用 middleware 链中下一个 middleware 的 dispatch。
let returnValue = next(action); // 一般会是 action 本身,除非后面的 middleware 修改了它。
console.log('state after dispatch', getState());
return returnValue;
};
};
};
const store = createStore(teacher, applyMiddleware(middleWare1, middleWare2));
store2.dispatch({
type: 'add',
payload: [1]
});
// will dispatch {type: "add", payload: Array(1)}
// will dispatch {type: "shfit", payload: Array(1)}
// state after dispatch (4) [2, 3, 4, 5]
// state after dispatch (4) [2, 3, 4, 5]
compose(function)
从右到左来组合多个函数作为enhancer传入createStore,调用时会以从左至右的顺序执行。
未传入function时,返回一个执行结果返回store的function
传入一个function是,返回这个function
传入多个function时,返回一个将传入的 function 从右到左包装的函数。
源码展示:
function compose() {
for (var _len = arguments.length, funcs = Array(_len), _key = 0; _key < _len; _key++) {
funcs[_key] = arguments[_key];
}
if (funcs.length === 0) {
return function (arg) {
return arg;
};
}
if (funcs.length === 1) {
return funcs[0];
}
return funcs.reduce(function (a, b) {
return function () {
return a(b.apply(undefined, arguments));
};
});
}
const teacher = (state = [1, 2, 3, 4, 5], action) => {
let result = [...state];
switch (action.type) {
case 'add':
result = [...result, ...action.payload];
break;
case 'shift':
result.shift();
break;
}
return result;
};
const middleWare1 = ({dispatch, getState }) => { // store的api dispatch、getState、replaceReducer、subscribe
return (next) => {
return (action) => {
console.log('will dispatch', action);
// 调用 middleware 链中下一个 middleware 的 dispatch。
let returnValue = next(action); // 一般会是 action 本身,除非后面的 middleware 修改了它。
console.log('state after dispatch', getState());
return returnValue;
};
};
};
const enhancer = () => {
return (next) => {
return (action) => {
const store = next(action);
const dispatch = store.dispatch;
store.dispatch = (action) => {
console.log('logger', action.type);
dispatch(action);
};
return store;
};
};
};
const store = createStore(teacher, compose(applyMiddleware(middleWare1), logger()));
store2.dispatch({
type: 'add',
payload: [1]
});
// will dispatch {type: "add", payload: Array(1)}
// logger add
// state after dispatch {student: Array(1), teacher: Array(6)}
bindActionCreators(action, dispatch)
组件可以在props中隐式调用dispatch
// root.jsx
import store from './store.js';
const action = {
add (data) {
return {
type: 'add',
payload: data
};
}
};
const custom = bindActionCreators(action, store.dispatch);
render(
,
document.getElementById('app')
);
// Page.jsx
this.props.add(['a']);
// will dispatch {type: "add", payload: ["a"]}
// logger add
// state after dispatch (6) [1, 2, 3, 4, 5, "a"]
源码展示:
function bindActionCreator(actionCreator, dispatch) {
return function () {
return dispatch(actionCreator.apply(undefined, arguments));
};
}
代码会返回一个被包装的 dispatch 方法,当在页面调用 this.props.add时,Redux 会把 add 返回的 action 传给 store.dispatch 方法,实现隐式调用的效果。