rematch是对redux的二次封装,简化了redux是使用,极大的提高了开发体验。rematch仅仅是对redux的封装,没有依赖redux-saga,也没有关联react,因此其可以用在其他的视图库中,如vue等。
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.countState
和this.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来写状态,同步和异步方法