@rematch/core——dispatch的简化版

rematch是对redux的二次封装,简化了redux是使用,极大的提高了开发体验。rematch仅仅是对redux的封装,没有依赖redux-saga,也没有关联react,因此其可以用在其他的视图库中,如vue等。

rematch-官方文档

1 一个简单的demo

项目结构

├── index.html
├── index.js          # 项目的入口
├── ...
└── store
    ├── index.js           # 引入modules的各个模块,初始化store的地方
    └── modules
        ├── count.js       # count模块
        └── xxx.js      # xxx模块

1.1. store/index.js,初始化一个store实例

import { init } from '@rematch/core';
import count from './modules/count';

const store = init({
    models: {
        count
    }
})

export default store;

rematch提供 init([config])方法,返回一个store的实例。初始化store的时候rematch支持传入多个模块,在中、大型项目中可以根据业务的需要,拆分成多个模块,这样项目的结果就会变得清晰明了。

1.2. rematch的model: store/modules/count.js

model中直接写state,reducers,effects,十分集中方便

const count = {
    state: {
        num: 1
    },
    reducers: {
        increment(state, num1, num2) { 
            /*
                从第二个变量开始为调用increment时传递进来的参数,后面依次类推,
                例如:dispatch.count.increment(10, 20)时, num1 = 10 , num2 = 20. 
            */          
              return  {
          ...state,
                num: num1
            }
        },
    },
    effects: {
        async incrementAsync(num1, rootState, num2) { 
            /*
                第二个变量rootState, 为当前model的state的值

                第一个变量num1, 第三个变量num2分别, 调用incrementAsync时传递进来的第一个参数, 第二个参数,后面依次类推。
                例如:dispatch.count.incrementAsync(10, 20)时,num1 = 10, num2 = 20
            */
            await new Promise(resolve => setTimeout(resolve, 2000));
            this.increment(num1);
        }
    }
}

export default count;

effects 也可以是方法, count模块可以改写成

const count = {
    state: {
        num: 1
    },
    reducers: {
        increment(state, num1, num2) {         
              return  {
          ...state,
                num: num1
            }
        },
    },
    effects: (dispatch) => ({
        async incrementAsync(num1, rootState, num2) {
            await new Promise(resolve => setTimeout(resolve, 2000));



            // 方式一
            // this.increment(num1); 

            // 方式二
            dispatch.count.increment(num1)

        }
    })
}

export default count;
  • state:

    • 存放模块状态的地方。
  • reducers:

    • 改变store状态的地方,每个reducers函数都会返回一个对象作为模块最新的state。
    • reducers中的函数必须为同步函数,如果要异步处理数据需要在effects中处理。
    • 注意:只能通过在reducers的函数中通过返回一个新的对象来改变模块中state的值,直接通过修改state的方式是是不能改变模块的state的值。例:
          increment(state, num1) {  
              state.num = num1  // 这样的写法是错误的
          },
    
  • effects:

    • 处理异步数据的地方,比如:异步从服务端获取数据。
    • 注意:在effects中是不能修改模块的state,需要在异步处理完数据后调用reducers中的函数修改模块的state。
  • rematch的state、reducers、effects和vuex的state、mutaition、action用法非常相似,在vuex中mutation修改model的state的值,action进行异步处理数据。

1.3. 在组件中获取state和修改state的值

有2种方法可以获取state和修改state:

(1)使用redux的高阶组件connect将state、reducers、effects绑定到组件的props上。

使用redux提供的高阶组件connect给App组件注册countState和countDispatch属性,其中countState对应的是count模块的state属性,countDispatch对应的是count模块的reducers和effects。在组件中使用this.props.countStatethis.props.countDispatch就可以访问到count模块提供的state和reducers、effects了。

import React, {Component} from 'react';
import {connect} from 'react-redux';

class App extends Component {
    handleClick = () => {
      const { countDispatch } = this.props;
      countDispatch.increment(10)
    };

    render() {
      const { countState } = this.props;
      return (           
          <div className="App" onClick={this.handleClick}>
              当前num为{countState.num},点我num加10           
          </div> 
      );
    };
}

const mapStateToProps = (state) => ({
    countState: state.count
})

const mapDispatchToProps = (dispatch) => ({
    countDispatch: dispatch.count
})

export default connect(mapStateToProps, mapDispatchToProps)(App);

(2)使用rematch提供的dispatch和getState。

getState:rematch提供的getState方法返回整个store的state对象,如果要单独访问count模块的state,只需要 getState().count即可。

dispatch:rematch提供的dispatch可以直接调用整个store中定义的reducers和effects

// reducers
dispatch({ type: 'count/increment', payload: 1 }) // state = { count: 1 }
dispatch.count.increment(1)                       // state = { count: 2 }// 其中count为store中的一个model,

// effects
dispatch({ type: 'count/incrementAsync', payload: 1 }) // state = { count: 3 } after delay
dispatch.count.incrementAsync(1)                       // state = { count: 4 } after delay

import React, {Component} from 'react';
import { dispatch, getState } from '@rematch/core';

class App extends Component {
    handleClick = () => {
     console.log(getState().count);//  {num: 1, a: 1}
        dispatch.count.increment(10)
    };

    render() {
        let countState = getState().count;

        return (
            <div className="App" onClick={this.handleClick}>
                当前num为{countState.num},点我num加10
            </div> 
        );
    };
}

1.4. 在一个model的reducers中的函数中触发另外一个model的reducers中的函数

场景:A函数和B函数里面有大量相似的代码,这个时候我们一般的做法都是将A、B函数的公共部分提取出来成一个公共函数,这样我们就不需要在A函数和B函数中写大量相似的代码。
假如在reducers中,我们将A函数和B函数提取的公共函数C放在公共模块info的reducers中,A函数是在count模块的reducers中。在这种情况下我们就需要在公共模块info的函数C执行完后调用count模块的A函数。

{  // count模块 
    reducers: {
        'info/addAge': (state, payload) => {  
            // payLoad的值为addAge传入的值10 
            return {
                ...state,
                num: 10
            }
        }
    },
}

{  // info模块 
    reducers: {
        addAge(state, num) {            
            return {
               age: state.age + num.age,
            }
        }
    }
    ...
}

  通过dispatch.info.addAge(10)调用info模块的addAge函数,当addAge函数执行完后会触发count模块的'info/addAge' ,并且 'info/addAge'的参数payload的值为调用info模块的addAge函数时传入的参数 10

2. rematch的优点

2.1. 省略了action types

不必再多次写字符串,使用model/method代替

2.2.省略了action creators

直接调用方法,不必再生产action type(原版dispatch方法返回{type, payload}),使用dispatch.model.method代替

2.3.省略了switch语句

调用model.method方法,不必判断action type ( 原本reduces中)

2.4.集中书写状态,同步和异步方法

在一个model中使用state,reducers和effects来写状态,同步和异步方法

上一篇:python学习笔记(9)——数据乱码的处理之重新编码和解码


下一篇:《第一行代码》学习笔记11-活动Activity(9)