前言:html
前段時間學習完react後,恰好就接到公司一個react項目的迭代,順便鞏固一下前段時間的學習成果。項目使用的是redux+react-router,將全部的數據都放在redux中,異步處理數據使用redux-saga。因爲是迭代項目,因此代碼風格仍是沿用以前項目的寫法,將全部的數據都放在redux中。寫redux的時候心中默默的吐槽了無數次,特別是每次從服務端異步獲取數據的時候心中都會默默的想 「我只是想實現從服務端取一個列表數據,而後保存在store中,爲何須要引入redux-saga,而且引入後寫法仍是很繁瑣(雖然流程很清晰明瞭,可是須要進行大量的複製粘貼)」 ,寫着寫着感受心態就快崩了 。後面通過同事的介紹,瞭解到有rematch這麼一個更好用的js狀態容器,終於能夠脫離redux了。vue
簡介:react
先看看rematch的官方介紹:vuex
Rematch是沒有boilerplate的Redux最佳實踐。沒有多餘的action types,action creators,switch 語句或者thunks。redux
rematch是在redux的基礎上再次封裝後成果,在rematch中咱們不用聲明action類型、action建立函數、thuks、store配置、mapDispatchToProps、saga。若是你習慣使用vuex,那麼你必定會喜歡上rematch的,由於rematch的寫法和vuex基本同樣。bash
一個簡單的demo:react-router
先看看咱們的項目結構:異步
├── index.html ├── index.js # 項目的入口
├── ....
└── store ├── index.js # 引入modules的各個模塊,初始化store的地方 └── modules ├── count.js # count模塊 └── info.js # info模塊
1. store/index.js,初始化一個store實例async
import { init } from '@rematch/core'; import count from './modules/count'; import info from './modules/info'; const store = init({ models: { count, info, } }) export default store;
rematch提供 init()方法,返回一個store的實例。初始化store的時候rematch支持傳入多個模塊,在中、大型項目中能夠根據業務的須要,拆分紅多個模塊,這樣項目的結果就會變得清晰明瞭。函數
2. store/modules/count.js,編寫count模塊業務代碼
const count = { state: { num: 1,
type: 2 }, 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) { // 第二個變量爲當前model的state的值,num1爲調用incrementAsync時傳遞進來的第一個參數,num2爲調用時傳遞的第二個參數,後面依次類推。例如:dispatch.count.incrementAsync(10, 20)時,num1 = 10, num2 = 20 await new Promise(resolve => setTimeout(resolve, 2000)); this.increment(num1); } } } export default count;
事實上咱們的count模塊就是一個對象,該對象包含三個屬性:state、reducers、effects。
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進行異步處理數據。
3. 在組件中獲取state和修改state的值
有2種方法能夠獲取state和修改state:(1)使用redux的高階組件connect將state、reducers、effects綁定到組件的props上。(2)使用rematch提供的dispatch和getState。
(1)使用redux的高階組件connect
使用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)dispatch和getState
getState:rematch提供的getState方法返回整個store的state對象,若是要單獨訪問count模塊的state,只須要 getState( ).count便可。
dispatch:rematch提供的dispatch能夠直接調用整個store中定義的reducers和effects。例:dispatch.count.increment(10) ,其中count爲store中的一個model,increment方法爲count模塊中提供的一個reducers。調用effects的方法和調用reducers的方法同樣。
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; console.log(countState); 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);
四、在一個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 console.log(payload) // 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
總結:
因爲rematch的寫法和vuex很類似,因此在接觸rematch的時候以爲很是熟悉,很好上手,具體有有哪些坑只有等下次項目中引入了rematch踩了才知道。