redux-actions
2018-10-26 本文已影响103人
别过经年
redux-actions@2.6.3
import { createActions } from "redux-actions";
describe("redux-actions", () => {
it("createActions", () => {
const actions = createActions(
{
INCREMENT: (amount = 1) => ({ amount }),
decrementGeek: async (amount = 1) => ({ amount: -amount })
},
{ prefix: "@redux", namespace: "--" }
);
const { increment, decrementGeek } = actions;
expect(increment()).toEqual({
type: "@redux/INCREMENT",
payload: { amount: 1 }
});
expect(decrementGeek()).toEqual({
type: "@redux/decrementGeek",
payload: new Promise(() => {})
});
});
});
namespace
官方文档只提到了namespace一次
image.png
初学者可能很难理解,react-router是这么干的@@router/LOCATION_CHANGE
type都会有@@router前缀,我们也想创建自己的前缀怎么办,react-actions有prefix字段,但是我们怎么控制前缀和后面action之间的分隔符,那么namespace的作用就来了。
一开始只是认为createActions的参数只能是个对象,其实不然,官方示例:
const { actionOne, actionTwo, actionThree } = createActions(
{
// function form; payload creator defined inline
ACTION_ONE: (key, value) => ({ [key]: value }),
// array form
ACTION_TWO: [
first => [first], // payload
(first, second) => ({ second }) // meta
]
// trailing action type string form; payload creator is the identity
},
'ACTION_THREE'//也就是说这边可以是个字符串,就会调用默认的action creator函数
);
expect(actionOne('key', 1)).to.deep.equal({
type: 'ACTION_ONE',
payload: { key: 1 }
});
expect(actionTwo('first', 'second')).to.deep.equal({
type: 'ACTION_TWO',
payload: ['first'],
meta: { second: 'second' }
});
expect(actionThree(3)).to.deep.equal({
type: 'ACTION_THREE',
payload: 3
});
看上面的例子以为createActions第二个参数才可以是字符串,其实所有的都可以是个字符串,d.ts文件
export function createActions<Payload>(
actionMapOrIdentityAction: ActionMap<Payload, any> | string,
...identityActions: Array<string | Options>
): {
[actionName: string]: ActionFunctionAny<Action<Payload>>
};
也就是说第一个参数也可以是字符串,那么我们这么批量定义actions就是完全没问题的
const { increment, decrement } = createActions("increment", "decrement");
createActions是怎样实现深层嵌套的?
const actionCreators = createActions({
APP: {
COUNTER: {
INCREMENT: [amount => ({ amount }), amount => ({ key: 'value', amount })],
DECREMENT: amount => ({ amount: -amount }),
SET: undefined // given undefined, the identity function will be used
},
NOTIFY: [
(username, message) => ({ message: `${username}: ${message}` }),
(username, message) => ({ username, message })
]
}
});
expect(actionCreators.app.counter.increment(1)).to.deep.equal({
type: 'APP/COUNTER/INCREMENT',
payload: { amount: 1 },
meta: { key: 'value', amount: 1 }
});
handleActions是怎样实现reducer的switch case变成一个个函数的?
handleActions源码:
import reduceReducers from 'reduce-reducers';
import invariant from 'invariant';
import isPlainObject from './utils/isPlainObject';
import isMap from './utils/isMap';
import ownKeys from './utils/ownKeys';
import flattenReducerMap from './utils/flattenReducerMap';
import handleAction from './handleAction';
import get from './utils/get';
export default function handleActions(handlers, defaultState, options = {}) {
invariant(
isPlainObject(handlers) || isMap(handlers),
'Expected handlers to be a plain object.'
);
const flattenedReducerMap = flattenReducerMap(handlers, options);
const reducers = ownKeys(flattenedReducerMap).map(type =>
handleAction(type, get(type, flattenedReducerMap), defaultState)
);
const reducer = reduceReducers(...reducers, defaultState);//一次执行所有的reducer
return (state = defaultState, action) => reducer(state, action);
}
reducer就是个纯函数,action匹配改变新的state就返回出去,不匹配就返回原来的state,
reduce-reducers就是返回函数,redux数据更新的时候,会使用reducer执行所有的handleActions的reducer函数,源码很简单,并不是什么黑科技