1.0.7 • Published 2 years ago

grace-redux v1.0.7

Weekly downloads
-
License
MIT
Repository
github
Last release
2 years ago

grace-redux

  • 优雅的redux
  • type 常量命名空间
  • action 自生成
  • dispatch 条理化
  • 简化 和 规范reducer、 saga 代码

温馨提示

  • 安装grace-redux 之后不需要再安装 redux 和 redux-saga 这两个库。
  • grace-redux 库 导出了 redux-saga 的 API ,可以直接使用。使用方式参考redux-saga 文档
  • 推荐使用ts以获得更好的代码提示体验

安装

npm i grace-redux

API

GraceRedux

基类,需要子类继承 GraceRedux 并实现它的属性和方法。

代码示例
testRedux.ts

import { GraceRedux } from 'grace-redux';

// 继承 GraceRedux 类
class TestRedux extends GraceRedux <{}, {}> {
  namespace = ''; // 命名空间分割符, 默认值:类名 + 8位[0-9,a-z]组成的随机字符串

  constructor() {
    super();
    this.init(); // 初始化子类,必须在 constructor 里面执行,该方法继承父类,封装了一系列初始化动作
  }

  reducerTypes = {}; // 定义reducer type 常量

  sagaTypes = {}; // 定义saga type 常量

  quickReducer = {}; // 定义 reducer

  takeEverySagas() {}; // 返回 takeEvery 监听事件

  takeLatestSagas() {}; // 返回 takeLatest 监听事件

  takeLeadingSagas() {}; // 返回 takeLeading 监听事件

  throttleSagas() {}; // 返回 throttle 监听事件

  debounceSagas() {}; // 返回 debounce 监听事件

  *takeCustomSagas() {} // 返回 take 监听事件
}

const testRedux = new TestRedux(); // 在当前文件创建出实例对象,可以使得引用改文件时的对象实例名统一
export { testRedux };

GraceRedux类内置的方法

init

子类数据初始化。 必须在构造函数中调用: this.init();

代码示例
testRedux.ts

import { GraceRedux } from 'grace-redux';

// 继承 GraceRedux 类
class TestRedux extends GraceRedux <{}, {}> {
  constructor() {
    super();
    this.init(); // 初始化子类,必须在 constructor 里面执行,该方法继承父类,封装了一系列初始化动作
  }
}

const testRedux = new TestRedux(); // 在当前文件创建出实例对象,可以使得引用改文件时的对象实例名统一
export { testRedux };
常量命名空间

namespace : 命名空间,默认值:类名 + 8位0-9,a-z组成的随机字符串(建议自己设置成文件目录地址)

reducerTypes

如果子类有创建state, 则需要实现该属性,自动生成对应的action type。调用 this.createReducerType 返回reducerTypes值。

createReducerType

子类快捷创建 Reducer Action 函数。 this.createReducerType({name: '', key1: '', key2: '', ...})
说明:

  • 实参值有用的只有key,value介设置为""即可。
  • 将参数key值经过处理之后生成新的值({同key: 新value})并赋值给reducerTypes
  • 自动生成 作用于 Reducer 的 ActionCreator 函数: this.reducerAction.name('张三')

调用代码示例
testRedux.ts

import { GraceRedux } from 'grace-redux';

interface ReducerStates {
  name: string; 
  age: number; 
  info: {addr: string}; 
  bikes: number[];
}

// 继承 GraceRedux 类
class TestRedux extends GraceRedux <ReducerStates, {}> {
  constructor() {
    super();
    this.init(); // 初始化子类,必须在 constructor 里面执行,该方法继承父类,封装了一系列初始化动作
  }

  reducerTypes = this.createReducerType({name:'', age: '', info: '', bikes: ''});
}

const testRedux = new TestRedux(); // 在当前文件创建出实例对象,可以使得引用改文件时的对象实例名统一
export { testRedux };
quickReducer

创建 reucer state

  • key值与reducerTypes的key值对应
  • value 值则是state的初始值 示例:
quickReducer = {
  name: '',
  info: { addr: '' },
};

上面的代码与下面代码作用相同

function name(state = '', action) {
  switch (action.type) {
    case '命名空间/name':
      return action.payload;
    default:
      return state;
  }
}

function info(state = { addr: '' }, action) {
  switch (action.type) {
    case '命名空间/info':
      return {...state, ...action.payload};
    default:
      return state;
  }
}

将产生的state属性 如下

{
  name: '',
  info: {
    addr: ''
  }
}

完整调用示例
testRedux.ts

import { GraceRedux } from 'grace-redux';

interface ReducerStates {
  name: string; 
  age: number; 
  info: {addr: string}; 
  bikes: number[];
}

// 继承 GraceRedux 类
class TestRedux extends GraceRedux <ReducerStates, {}> {
  constructor() {
    super();
    this.init(); // 初始化子类,必须在 constructor 里面执行,该方法继承父类,封装了一系列初始化动作
  }

  reducerTypes = this.createReducerType({name: '', age: '', info: '', bikes: ''});

  quickReducer = {
    name: '',
    age: 0,
    info: { addr: '' },
    bikes: [],
  };
}

const testRedux = new TestRedux(); // 在当前文件创建出实例对象,可以使得引用改文件时的对象实例名统一
export { testRedux };

rootStroe.ts

import { startRedux } from 'grace-redux';
import { testRedux } from './testRedux';

const store = startRedux({ testRedux: testRedux.reducer }, [], true); // 启动 redux

将创建完整的state结构:store.getState()

{
  testRedux: {
    name: '',
    info: {
      addr: ''
    }
  }
}
reducerAction

reducerAction方法,在使用this.createReducerType时自动创建。 该方法实际上就是一个 reducer action creator 使用示例 this.reducerAction.name('张三')。 其中的 name 属性名与在调用this.createReducerType时的对象key值一致。 该方法返回值:

{
  type: '命名空间/name',
  payload: '张三'
}

代码使用示例 testRedux.reducerAction.name('张三')

rootStroe.ts

import { startRedux } from 'grace-redux';
import { testRedux } from './testRedux';

const store = startRedux({ testRedux: testRedux.reducer }, [ ...testRedux.saga, ], true);

store.dispatch(testRedux.reducerAction.name('张三'));

export default store;

结果:state.testRedux.name = '张三'。 除通过实例化对象调用。还可以在子类中调用。 例如:在saga函数中yield put(this.reducerAction.name(action.payload));

sagaTypes

如果子类有注册saga函数,则需要实现该属性。生成 action type 使用,用来与saga函数绑定。 调用 this.createSagaType 返回sagaTypes值。

createSagaType

使用方式与 createReducerType 相同,不同的是内部对数组的处理方式不同。自动生成 作用于 saga 的 ActionCreator 函数 代码示例

sagaTypes = this.createSagaType({fetchName: '', key1: '', key2: '', ...});
takeEverySagas

该方法是对 takeEvery 方法的封装。 子类需要注册的 takeEvery saga 函数 需要在这里定义 使用this.sagaTypes.fetchName 作为函数名,函数将自动与对应的 action type 绑定。 testRedux.ts

interface SagaTypes {
  fetchName: string;
}

class TestRedux extends GraceRedux<{}, SagaTypes> {
  ...
  sagaTypes = this.createSagaType({ fetchName: '', });

  takeEverySagas = {
    [this.sagaTypes.fetchName]: function* (action) {
      const payload = action.payload;
    },
    ...
  };
}

rootStroe.ts

import { startRedux } from 'grace-redux';
import { testRedux } from './testRedux';

const store = startRedux({ testRedux: testRedux.reducer }, [ ...testRedux.saga, ], true);

store.dispatch(testRedux.sagaAction.fetchName('李四'));

export default store;

testRedux.sagaAction.fetchName('李四')实际上返回一个action

{
  type: '命名空间/fetchName',
  payload: '李四'
}

触发 takerEverySaga 的 this.sagaTypes.fetchName 事件。

takeLatestSagas

该属性是对 takeLatest 方法的封装,使用方式与 takeEverySagas 属性相同

takeLeadingSagas

该属性是对 takeLeading 方法的封装,使用方式与 takeEverySagas 属性相同

throttleSagas

该属性是对 throttle 方法的封装, 函数的注册方式如下

throttleSagas = {
  [this.sagaTypes.fetchBikes]: {ms: 10, gen: function* (action) {
    yield put(this.reducerAction.bikes(action.payload));
  }},
};

takeEverySagas不同的地方在于属性值是一个对象,该对象有两个属性 ms 和 gen ms: 时间 gen: Generator 函数 触发方式与 takeEverySagas 属性相同

debounceSagas

该属性是对 debounce 方法的封装,使用方式与 throttleSagas 属性相同

*takeCustomSagas

自定义监听事件。redux-saga 帮我们实现了 五种监听事件的方式 takeEvery、takeLatest、takeLeading、throttle、debounce。如果业务上不满足,可以自定义自己的take事件类型。

如果子类有需要自定义的take事件,需要通过 在*takeCustomSagas中注册才会被监听。

示例代码 utils.ts

import { ReduxSagaEffects } from 'grace-redux';

const { fork, take } = ReduxSagaEffects;

// 自定义take事件
export function takeEveryOne(patternOrChannel, saga, ...args) {
  return fork(function* () {
    while (true) {
      const action = yield take(patternOrChannel);
      yield fork(saga, ...args.concat(action));
    }
  });
}

testRedux.ts

import { GraceRedux } from 'grace-redux';
import { takeEveryOne } from './utils';

class TestRedux extends GraceRedux <{}, {fetchName: string}> {

  sagaTypes = this.createSagaType({fetchName: ''});

  *takeCustomSagas() {
    yield takeEveryOne(this.sagaTypes.fetchName, function* (action) {
      yield console.log('自定义take', action);
    });
  }
}

工具 API

startRedux

快捷启动redux的方法。返回启动后的store.

startRedux(reducers, sagas, dev)

参数

  • reducers : object 类型,注册GraceRedux 子类的reducer
  • sagas : 数组,注册GraceRedux 子类的saga
  • dev : boolean 类型, 是否为开发模式,true 为开启 chrome 的插件 React Developer Tools

返回值

  • store

代码示例
rootStroe.ts

import { startRedux } from 'grace-redux';
import { testRedux } from './testRedux';

const store = startRedux({ testRedux: testRedux.reducer }, [ ...testRedux.saga, ], true); // 启动 redux
quickState

快捷获取redux 的 state 的属性值

quickState(store, args, defaultValue)

参数

  • store : redux启动之后返回的store对象
  • args : Array 'a', 'b', 'c' | string 'a.b.c'
  • defaultValue : 取不到值的默认值

返回值

  • state.a.b.c 的值 或者 defaultValue

代码示例

const cState =  quickState(store, 'a.b.c', []);
ReduxSagaEffects

redux-saga/effects 的 API 集合 文档地址:redux-saga/effects

代码使用示例

import { ReduxSagaEffects } from 'grace-redux';

const { put, fork, take, select } = ReduxSagaEffects;

与下面的使用方式等价

import { put, fork, take, select } from 'redux-saga/effects';
ReduxSaga

redux-saga 的 API 集合 文档地址:redux-saga

代码使用示例

import { ReduxSaga } from 'grace-redux';

const createSagaMiddleware = ReduxSaga;

与下面的使用方式等价

import createSagaMiddleware from 'redux-saga';

各API使用方式完整的示例代码

utils.ts

import { ReduxSagaEffects } from 'grace-redux';

const { fork, take } = ReduxSagaEffects;

// 自定义take事件
export function takeEveryOne(patternOrChannel, saga, ...args) {
  return fork(function* () {
    while (true) {
      const action = yield take(patternOrChannel);
      yield fork(saga, ...args.concat(action));
    }
  });
}

demoRedux.ts

import { GraceRedux, ReduxSagaEffects } from 'grace-redux';
import { takeEveryOne } from './utils';

const { put, fork, take, select } = ReduxSagaEffects;

interface ReducerState {
  name: string;
  age: number;
  info: { addr: string; }
  bikes: number[]
}

interface SagaTypes {
  fetchName: string;
  fetchAge: string;
  fetchInfo: string;
  fetchBikes: string;
  fetchState: string;
}
class DemoRedux extends GraceRedux<ReducerState, SagaTypes> {
  namespace = 'src/routes/demo';

  constructor() {
    super();
    this.init();
  }

  reducerTypes = this.createReducerType({name: '', age: '', info: '', bikes: ''});

  sagaTypes = this.createSagaType({fetchName: '', fetchAge: '', fetchInfo: '', fetchBikes: '', fetchState: ''});

  quickReducer = {
    name: '',
    age: 0,
    info: { addr: '' },
    bikes: [],
  };

  takeEverySagas(){
    const that = this;
    return {
      [this.sagaTypes.fetchName]: function* (action) {
        yield put(that.reducerAction.name(action.payload));
      },
    };
  }

  takeLatestSagas(){
    const that = this;
    return {
      [this.sagaTypes.fetchAge]: function* (action) {
        yield put(that.reducerAction.age(action.payload));
      },
    };
  }

  takeLeadingSagas(){
    const that = this;
    return {
      [this.sagaTypes.fetchInfo]: function* (action) {
        yield put(that.reducerAction.info(action.payload));
      },
    };
  }

  throttleSagas(){
    const that = this;
    return {
      [this.sagaTypes.fetchBikes]: {ms: 10, gen: function* (action) {
        yield put(that.reducerAction.bikes(action.payload));
      }},
    };
  }

  debounceSagas() {
    return {
      [this.sagaTypes.fetchState]: {ms: 10, gen: function* () {
        const state = yield select();
        console.log(state.demoRedux);
      }},
    };
  }

  *takeCustomSagas() {
    yield takeEveryOne(this.sagaTypes.fetchName, function* (action) {
      yield console.log('自定义take', action);
    });
  }
}

function takeEveryOne(patternOrChannel, saga, ...args) {
  return fork(function* () {
    while (true) {
      const action = yield take(patternOrChannel);
      yield fork(saga, ...args.concat(action));
    }
  });
}

const demoRedux = new DemoRedux();
export { demoRedux };

rootStroe.ts

import { startRedux } from 'grace-redux';
import { demoRedux } from './demoRedux';

const store = startRedux({ demoRedux: demoRedux.reducer }, [ ...demoRedux.saga, ], true);

store.dispatch(demoRedux.reducerAction.name('张三'));
store.dispatch(demoRedux.reducerAction.age(20));
store.dispatch(demoRedux.reducerAction.info({addr: '广东'}));
store.dispatch(demoRedux.reducerAction.bikes([1, 2]));
store.dispatch(demoRedux.sagaAction.fetchAge(30));
store.dispatch(demoRedux.sagaAction.fetchName('李四'));
store.dispatch(demoRedux.sagaAction.fetchInfo({addr: '北京'}));
store.dispatch(demoRedux.sagaAction.fetchBikes([1, 2, 3]));
store.dispatch(demoRedux.sagaAction.fetchState());

export default store;
1.0.7

2 years ago

1.0.6

2 years ago

1.0.5

2 years ago

1.0.4

2 years ago

1.0.3

2 years ago

1.0.2

2 years ago

1.0.0

3 years ago