redux

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函数,源码很简单,并不是什么黑科技

上一篇 下一篇

猜你喜欢

热点阅读