redux-watchman
TypeScript icon, indicating that this package has built-in type declarations

1.1.1 • Public • Published

redux-watchman

redux-watchman 是一个基于async + await用于管理异步数据流的redux中间件。api的设计灵感来源于redux-saga,但是他:

  • 轻便 —— 经过转义压缩后仅 3KB
  • 实用 —— 根据saga常用的api进行设计,在async + await 普及的今天,它能带来不一样的开发体验。
  • 简易 —— 如果您是saga的用户,且熟悉async+await语法那可以说是0学习成本,如果您是新手,简易的demo也能让你快速入手。
  • 可靠 —— 作者已将其用在公司若干个项目中,线上稳定运行。
  • 友好 —— redux-watchman使用 typescript 编写,且配置好 d.ts文件,能提供良好的编码提示。

如果你是 redux 的使用者,且在项目中习惯使用异步流管理数据交互,redux-watchman将会是你不错的选择。

开始

安装

$ npm install redux-watchman -S

或者你可以在githubclone下本项目然后:

$ npm i
$ npm run build:umd

通过script标签引入umd文件夹下的js文件。

使用示例

下面我们通过一个通讯录的部分crud来描述:

// reducer.js
 
// 设置通讯录列表
const set = (state,action)=>{
  return {
    contacts: action.payload
  }
}
// 新增/修改联系人
const edit = (state,action)=>{
  const contacts = state.contacts;
  const index = contacts.findIndex(contact=>contact.id === action.payload);
  if(index !== -1) {
    contacts.splice(index, 1, action.payload);
    return {
      contacts: [...contacts]
    }
  }
  return {
    contacts: [...contacts, action.payload]
  }
}
// ... 省略del
const reducer = (state,action)=>{
  const handler = {
    set,
    edit,
    del
  }
  if(handler[action.type]) return handler[action.type](state,action);
  return state
}
export default reducer;
// watchman.js
import { all, put, select, take, takeEvery } from 'redux-watchman';
// 用来模拟异步请求
const requestMock = function(data){
  return Promise.resolve(data);
}
// 用来生成单个人员
const contactFactory = function(name = 'lee', phone = '123'){
  return {
    id: Math.random() * 100,
    name,
    phone
  }
}
// 下面开始定义监听的各个action
// 查询联系人
const query = async function(){
  // 仅监听一次type为query的action。因为大列表的数据一般只需要查询一次
  await take('query');
    
  // 模拟异步过程
  const mock = [
    contactFactory('lee', '123'),
    contactFactory('zhao', '123')
  ]
  const payload = await requestMock(mock);
  // 往reducer上抛
  await put({ type: 'set', payload})
}
// 新增/修改联系人
const edit = async function(){
  // 多次监听type为editAsync的action,因为这个会频繁操作,监听回调会接收当前action作为参数
  await takeEvery('editAsync', async function(action){
    let mockData = action.payload;
    // 根据id判断新增还是修改
    if(!action.payload.id){
      mockData = {...mockData, id: Math.random()}
    }
    // 模拟异步
    const payload = await requestMock(mockData);
    // 同上
    await put({ type: 'edit', payload})
  })
}
// 异步删除操作
const del = async function(){
  await takeEvery('delAsync', async function(action){
    if(!action.payload.id)return
    const result = await requestMock(true);
    if(result)await put({ type: 'del', payload: action.payload.id})
  })
}
// 删除最后一个
const pop = async function(){
  await takeEvery('popAsync', async function(action){
    // 先找到最后一个然后删除
    const contacts = await select(function(state){return state.contacts});
    const last = contacts[contacts.length - 1];
    const result = await requestMock(true);
    if(result)await put({ type: 'del', payload: last.id})
  })
}
 
// 把响应的方法整合,通过一个接口导出
const root = async function(){
  await all([ query, edit, del, pop ])
}
 
export default root;
 
 
import { createStore, applyMiddleware } from 'redux';
import { createWatchman } from 'redux-watchman';
import reducer from './reducer';
import root from './watchman';
// 先创建一个watchman
const watchman = createWatchman();
const store = createStore(
  reducer,
  {
    contacts: []
  },
  applyMiddleware(
    watchman.watchmanMiddleware // 加入middleware
  )
)
// run之前定义的所有方法
watchman.run(root);
 
 
// 接下来描述这样一个过程
// 定义一个数据打印方法,方便查看每个dispatch后的数据变化。
const withStoreShow = (f)=>{
  f();
  return new Promise(resolve=>{
    setTimeout(()=>{
      console.log('state');
      console.log(JSON.stringify(store.getState()));
      console.log('-----------------------')
      resolve()
    }, 200);
  })
}
 
const run = async function(){
  // query
  await withStoreShow(()=>{
    console.log('query');
    store.dispatch({ type: 'query' })
  });
  // 新增
  await withStoreShow(()=>{
    console.log('editAsync');
    store.dispatch({ type: 'editAsync', payload: {name: 'wang', phone: '333'}})
  });
  // 修改
  await withStoreShow(()=>{
    console.log('editAsync');
    const state = store.getState();
    store.dispatch({ type: 'editAsync', payload: {id: state.contacts[0].id, name: 'll', phone: '773'}})
  });
 
  // 删除
  await withStoreShow(()=>{
    console.log('delAsync');
    const state = store.getState();
    store.dispatch({ type: 'delAsync', payload: state.contacts[0].id})
  });
  // 删除最后一个
  await withStoreShow(()=>{
    console.log('pop');
    store.dispatch({ type: 'popAsync' })
  });
}
 
run();

文档

以下均为函数

名称 参数 描述
take string 接收一个字符串,作为参数,仅监听一次type值为该字符串的action。返回一个promise,当该type值的actiondispatch时,promise变为resolved状态,Promise值为当前action
takeEvery string, asyncFunction 多次take;当监听到对应action时,调用传入的async方法,在当前async方法返回的promise状态变更之前,再次dispatch相同typeaction,方法不会响应。返回一个状态永远为penddingPromise
put action 可以简单的理解为store.dispath,返回一个PromisePromise值为当前action
select (state)=>{} select接收一个函数ff能拿到当前state作为参数,select返回一个Promise,该Promise的值为函数f的返回值。
all asyncFunction[] 可以简单的理解为Promise.all
createWatchman / 不需要传参,调用后返回{ run, watchmanMiddleware },需要将watchmanMiddleware加入到redux middleware中并初始化reduxrun接收一个asyncFunction,用于启动上面定义的effect。

License

MIT

Package Sidebar

Install

npm i redux-watchman

Weekly Downloads

0

Version

1.1.1

License

MIT

Unpacked Size

19.6 kB

Total Files

5

Last publish

Collaborators

  • leekk