npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

regular-redux-util

v1.0.16

Published

regularjs X redux

Downloads

4

Readme

regular-redux-util

regularjs X redux

使用步骤

一、将store绑定到外层组件

1、创建 store

import ReduxUtil from 'regular-redux-util';

// 外层组件
let WrapperComponent = BaseComponent.extend({
  // ...
  config(data) {
    // 初始化&绑定 store
    data.store = ReduxUtil.createStore(
      reducer, // reducer
      initialState, // 可选。普通对象{},为store设置一个初始值
      // 可选。中间件,默认值为: thunk 和 promise
      [ReduxUtil.middleware.thunk, ReduxUtil.middleware.promise],
      // 可选。其他可配置参数
      {
        isDev: window.DEBUG, // 可选。当前是否为开发环境
        disableImmutableJS: false // 可选。是否不支持immutable,默认为false,表示支持
      }
    );
  },
  //...
});

// 为外层组件添加 StoreProvider
WrapperComponent.use(ReduxUtil.StoreProvider);
参数解释
  1. reducer:符合redux定义的一个reducer函数 or 对象(使用多个reducer时)。注意:reducer 函数要由 ReduxUtil.createReducer 创建。对象可以由多个reducer函数拼装得到。
  2. initialState:可选。普通对象{},为store设置一个初始值。
    • 建议一定要写,开发时可以很方便的查看state的数据结构
  3. middlewares:可选,中间件,默认会提供 thunk 和 promise 中间件。当有值的时候,默认会被覆盖。在开发环境还会提供logger中间件。
    • promise中间件api参考自:https://github.com/pburtchaell/redux-promise-middleware 5.x版
    • 开发环境的判断见下面的第4点
  4. config:可选,一些配置参数。
    • isDev:可选。当前是否为开发环境。默认值由:window.DEBUG || /localhost|\d+.\d+.\d+.\d+/.test(location.hostname) 来确定
    • disableImmutableJS:可选。是否不支持immutable,默认为false,表示支持

2、外层组件的模板

<!-- 外层包裹 StoreProvider,并绑定 store 字段 -->
<StoreProvider store={store}>
  
  <sub-component></sub-component>
  <sub-component2></sub-component2>
  ...
  
</StoreProvider>

二、action

1、创建普通的action

// action-factory.js

import ReduxUtil from 'regular-redux-util';

// 方式一
let actions;
const actionObj = {
  // 图片模块,切换主图
  changeImg(bigImg, selectIndex) {
    return {
      payload: {
        bigImg, selectIndex
      }
    };
  }
};
actions = ReduxUtil.createAction(actionObj);

// 方式二
let actions = ReduxUtil.createAction({});
actions.add('updateAddress', address => { payload: address });

2、创建异步请求(有副作用)的action

// 1、使用 promise 中间件( payload 的值为一个 promise )
// 获取地址
getAddress(query = {}) {
  return {
    payload: service.getAddress(query)
      .then((json = {}) => {
        return (json.retcode === 200) ?
          Promise.resolve(json.addressDto || C.DEF_ADDRESS) :
        Promise.resolve(C.DEF_ADDRESS);
      })
      .catch((err) => {
        return Promise.resolve(C.DEF_ADDRESS);
      })
  };
}

// 2、如果参数需要从 state 中获取,可以 thunk 和 promise 中间件结合使用
// 获取异步数据
initAsyncData(address) {
  // thunk action
  return (dispatch, getState) => {
    let state = getState();
    let query = Object.assign({}, address, {
      goodsId: state.get('goodsId'),
      categoryId: state.get('categoryId')
    });
    // promise action
    return dispatch(actions.initAsyncDataService(query));
  };
},
// 真正的promise类型的action
initAsyncDataService(query) {
  return {
    payload: service.getAsyncData({
      query
    })
    .then((json = {}) => {
      if (json.code === 200) {
        return Promise.resolve(json.data);
      }
      return Promise.reject(json || {});
    })
    .catch((err) => {
      return Promise.reject(err);
    })
  };
}
promise中间件
  • api参考自:https://github.com/pburtchaell/redux-promise-middleware 5.x版
  • dispatch该action之后,会立即触发所绑定的reducer中的pedding
  • promise对象如果返回Promise.resolve,则触发所绑定的reducer中的fulfilled
  • promise对象如果返回Promise.reject,则触发所绑定的reducer中的rejected
  • 后面会介绍如何定义reducer,以及pedding、fulfilled、rejected的reducer处理函数

三、reducer

1、创建reducer,绑定action

import ReduxUtil from 'regular-redux-util';

// 之前创建的action
import {
	  changeImg, // 普通
    getAddress, // 异步
    initAsyncDataService // 异步
} from '../action-factory';

// reduer 实例
let reducer = ReduxUtil.createReducer();

// 普通的action绑定的reducer
reducer.add(changeImg, (state, action) => {
  return state;
});

// 一个异步action绑定的3个reducer
reducer.add(getAddress.pending, (state, action) => {
  // ...
  return state;
});
reducer.add(getAddress.fulfilled, (state, action) => {
  // ...
  return state;
});
reducer.add(getAddress.rejected, (state, action) => {
  // ...
  return state;
});

// 另一种写法,集合在一起
reducer.add(getAddress, {
  pending(state, action) => state,
  fulfilled(state, action) => state,
  rejected(state, action) => state,
});

// 另另种,将错误的情况统一处理
reducer.add([
  getAddress.rejected,
  initAsyncData.rejected,
], state => {
  //...
  return state;
});

2、在reducer中调用其他reducer

reducer就是一个纯函数,为了更好的复用,可以在一个reducer中调用其他reducer

import ReduxUtil from 'regular-redux-util';

// 之前创建的action
import {
	  changeImg, // 普通
    getAddress, // 异步
    initAsyncDataService // 异步
} from '../action-factory';

// ...

// 异步数据获取成功
reducer.add(initAsyncDataService.fulfilled, (state, action = {}) => {
  // ... bigImg, selectIndex
  
  // 切换主图
  // 调用 changeImg 绑定的 reducer
  //  - 注意:返回值要赋值给 state,否则无法修改state的结果
  //  - 第三个参数:{bigImg, selectIndex},是传入到`changeImg`这个action的第二个参数(action对象)
  state = reducer.exec(changeImg, state, {bigImg, selectIndex});
  
  // ...
  return state;
});

// ...

四、container组件

container组件,就是绑定了redux的regular组件。

当redux的state发生变化时(dispatch了action,触发reducer之后,state就应当发生变化),会触发所有container组件的更新

更新:拉取新的state上的数据,更新regular中的data变量,最后调用 this.$update()

一个container组件的创建步骤如下:

1、引入需要的action和reduxUtil


import ReduxUtil from 'regular-redux-util';
import {
    initSyncData,
    getAddress,
    initAsyncData,
    triggerVidList,
    buyNow
} from '../action-factory';

2、创建regular组件


let Common = DetailComponent.extend({
    template,
    data: {
        selVidList: [] // 已选择的sku
    },
    config() {},
    init() {
    },
    //...
});
export default Comment;

3、将regular组件绑定到redux

修改export default Comment;


// 组件对象 `Comment`
export default ReduxUtil.connect({
    mapState(state) {
        // 已选择的sku
        let selVidList = state.getIn(['skuProps', 'selVidList']);
        return {
            selVidList
        };
    },
    // 【可选】state改变后是否触发组件的刷新
    listenStateChange: ['skuProps', 'async.goodsCouponList'],
    // 【可选】绑定到this上的action
    mapDispatch: [
        initSyncData,
        getAddress,
        initAsyncData,
        triggerVidList,
        buyNow
    ],
    //【可选】
    options: {
        // 自动emit事件:
        // this.$emit('rdx-connected')
        // this.$emit('rdx-afterchanged')
        // this.$emit('rdx-injected')
        emit: true
    }
})(Common);
参数解释:
  1. mapState:state改变后,将会执行所有组件的mapState函数。函数的参数是redux的state。函数的返回值newMappedState将会使用newMappedState && Object.assign(this.data, newMappedState);更新组件的data,并最终通过this.$udpate()更新组件的视图。注意,当使用不可变数据类型(disableImmutableJS: false)的时候,state是一个immutable对象。
  2. listenStateChange:【该参数可选】mapState的模式可能会引起性能问题。因为reducer触发了state更新之后,会触发所有container组件的mapState。因此可以通过listenStateChange参数来做一次筛选,只有当listenStateChange中的数据发生变化时,才会执行mapState函数。比如在上面的例子里,只有当state.skuPropsstate.async.goodsCouponList发生改变的时候,才会执行mapState函数。
  3. mapDispatch:【该参数可选】container组件中,如果需要dispatch一个action,那么需要将这个action函数写到mapDispatch这个参数下面。比如initSyncData然后就可以通过this.initSyncData(window.__syncData || {});来dispatch这个action了。如果不使用mapDispatch参数,也可以直接适用this.$dispatch,见下文步骤4
  4. options:{emit: boolean}:【该参数可选】emit参数默认为false。当emit设置为true的时候,会在cotainer组件的生命周期中,自动emit三个事件。
    • rdx-connected:只触发一次,在首次mapState执行之后
    • rdx-afterchanged:每次执行mapState后都会触发
    • rdx-injected:只执行一次,当组件可以获取this.$refs.xx的之后触发。如果组件被实例化多次,也会被触发多次。

4、在组件中dispatch action


// 方法一
...
init() {
    // dispatch action
    this.$dispatch(initSyncData(window.__syncData || {}));
},
...

// 方法二
//  如果在上一步`ReduxUtil.connect`中使用了mapDispatch
//  并注册了`initSyncData`这个action,那么可以使用方法二,如下:
...
init() {
    // dispatch action
    this.initSyncData(window.__syncData || {});
},
...

五、将container添加到外层组件

import Common from './redux-base/container/common';

WrapperComponent.component('Common', Common);
<StoreProvider store={store}>
  ...
    <Common
        on-loaded={this.loaded($event)}
        on-refreshTopBar={this.refreshTopBar($event)}
        ></Common>
  ...
</StoreProvider>

到这里,使用步骤就介绍完了

实践

一、关于使用不可变数据类型

二、使用initialState

三、action的粒度

四、使用listenStateChange可能遇到的问题