redux探索:rematch

redux存在的问题

  • 项目中redux的样板文件太分散,书写和维护都比较麻烦react

  • 使用thunk来处理异步操做,不是那么直观git

方案对比

基于redux数据流的管理方案:Dvamirrorrematchgithub

Dva

Dva是蚂蚁金服开源的一个数据流管理方案,基于redux和redux-saga,简化了开发体验。Dva是一揽子的解决方案,可使用侵入性很强的dva-cli来快速搭建项目,提供了路由层面的适配;也可使用dva-core来引入核心的代码,减小侵入性。json

缺点
  • 若是使用Dva的一整套框架,现有的项目会有较大的改动redux

  • Dva使用redux-saga来处理异步,学习成本比较高promise

mirrorbash

相似于Dva的一个redux数据流方案,最新一次更新在两个月以前,一直没有发布1.0的版本react-router

rematch框架

rematch的灵感来自于Dva和mirror,将二者的有点结合了起来。less

优势
  • 使用了相似Dva的model文件结构,统一管理同步和异步操做

  • 经过中间键实现了async/await的方式来处理异步,舍弃了Dva中的redux-saga

  • 提供了redux的配置项,能够兼容项目中的老代码

  • 支持多个store

缺点?
  • 将model中reducers和effects的方法挂载在dispatch函数上,形成dispatch既是一个函数,又是一个对象


Rematch Mirror Dva
适用框架 全部框架 / 不使用框架 React React
适用路由 全部路由 / 不使用路由 RR4 RR3, RR4 / 不使用路由
移动端 ×
开发者工具 Redux, Reactotron Redux Redux
插件化
reducers
effects async/await async/await redux saga
effect params (payload, internals) (action, state) (action, state)
监听方式 subscriptions hooks subscriptions
懒加载模型
链式 dispatch
直接 dispatch
dispatch promises
加载插件
persist plugin
package size 14.9k(gzipped: 5.1k)|| redux + thunk: 6k(2k) 130.4k(gzipped: 33.8k) dva-core: 72.6k(gzipped: 22.5k)

rematch

API

import { init } from '@rematch/core';
  ​
  const store = init({
    models: {
      count: {
        state: 0,
        reducers: {
          add: (state, payload) => state + payload,
          del: (state, payload) => state - payload,
          'otherModel/actionName': (state, payload) => state + payload,
        },
        effets: {
          async loadData(payload, rootState) {
            const response = await fetch('http://example.com/data')
            const data = await response.json()
            this.add(data)
          }
        }
      }
      list: {}
    },
    redux: {
      reducers: {},
      middlewares: [thunk],
    },
    plugins: [loading]
  })复制代码

init

对rematch进行初始化,返回一个store对象,包含了使用redux初始化store对象的全部字段。

models: { [string]: model }

一个对象,属性的键做为rootState上的的键

model.state: any

用来初始化model

model.reducers: { [string]: (state, payload) => any }

一个对象,属性是用来改变model state的方法,第一个参数是这个model的上一个state,第二个参数是payload,函数返回model下一个state。这些方法应该是纯函数。

model.effects: { [string]: (payload, rootState) }

一个对象,异步或者非纯函数的方法放在这个对象中,能够与async/await一块儿使用

redux

经过这个属性,能够兼容老项目中的redux配置。

plugins

rematch是一个插件系统,经过这个字段能够配置第三方的插件。

redux流程:


rematch流程:

例子

##index.js
  import React from 'react'
  import ReactDOM from 'react-dom'
  import { Provider } from 'react-redux'
  import { init } from '@rematch/core'
  import App from './App'
  ​
  const count = {
    state: 0,
    reducers: {
      increment: s => s + 1,
    },
    effects: dispatch => ({
      async asyncIncrement() {
        await new Promise(resolve => {
          setTimeout(resolve, 1000)
        })
        dispatch.count.increment()
      },
    }),
  }
  ​
  const store = init({
    count,
  })
  ​
  // Use react-redux's <Provider /> and pass it the store. ReactDOM.render( <Provider store={store}> <App /> </Provider>, document.getElementById('root') ) ​ ​ ##App.js import React from 'react' import { connect } from 'react-redux' ​ // Make a presentational component. // It knows nothing about redux or rematch. const App = ({ count, asyncIncrement, increment }) => ( <div> <h2> count is <b style={{ backgroundColor: '#ccc' }}>{count}</b>
      </h2>
  ​
      <h2>
        <button onClick={increment}>Increment count</button>{' '}
        <em style={{ backgroundColor: 'yellow' }}>(normal dispatch)</em>
      </h2>
  ​
      <h2>
        <button onClick={asyncIncrement}>
          Increment count (delayed 1 second)
        </button>{' '}
        <em style={{ backgroundColor: 'yellow' }}>(an async effect!!!)</em>
      </h2>
    </div>
  )
  ​
  const mapState = state => ({
    count: state.count,
  })
  ​
  const mapDispatch = dispatch => ({
    increment: dispatch.count.increment,
    asyncIncrement: dispatch.count.asyncIncrement,
  })
  ​
  // Use react-redux's connect export default connect( mapState, mapDispatch )(App) ​复制代码

老项目接入

主要针对已经使用thunk中间键的老项目。

一、安装依赖,并删除依赖中的redux

yarn add @rematch/core

yarn remove redux (删除redux可能会形成eslint报错)

二、修改redux入口文件

src/store/index.js
  ​
  import { init } from '@rematch/core';
  import thunk from 'redux-thunk';
  import reduxReducerConfig from '@/reducers';
  import models from '../models';
  ​
  const store = init({
    models,
    redux: {
      reducers: {
        ...reduxReducerConfig
      },
      middlewares: [thunk],
    },
  });
  ​
  export default store;复制代码

三、修改reducers的入口文件

import { routerReducer as routing } from 'react-router-redux';
  - import { combineReducers } from 'redux';
  import dispatchConfigReducer from './dispatch-config';
  import counterReducer from './count';
  ​
  - export default combineReducers({
  -   routing,
  -   dispatchConfigReducer,
  -   counterReducer,
  - });
  ​
  + export default {
  +   routing,
  +   dispatchConfigReducer,
  +   counterReducer,
  + };复制代码

四、增长model的入口文件

+ src/models
  + src/models/re-count.js
  + src/models/config-list.js
  + src/models/index.js
  ​
  index.js
  ​
  import reCount from './re-count';
  import configList from './config-list';
  ​
  export default {
    reCount,
    configList,
  };复制代码

若是老项目中没有使用redux,可使用yarn remove thunk删除thunk的依赖和reducers这个文件夹,而且在init初始化的时候能够不用传redux这个配置。

若是接入rematch,须要锁定版本,rematch中引入的redux版本为4.0.0,因此老项目中的redux要更新为为4.0.0,否则打包的时候会把两个版本的redux都打进去。

新项目配置

index.js
  ​
  import React from 'react';
  import { render } from 'react-dom';
  import { browserHistory, Router } from 'react-router';
  import { syncHistoryWithStore } from 'react-router-redux';
  import { Provider } from 'react-redux';
  import routes from '@/routes';
  import store from '@/store';
  import '@/styles/index.less';
  ​
  const history = syncHistoryWithStore(browserHistory, store);
  ​
  render(
    <Provider store={store}>
      <Router history={history} routes={routes} />
    </Provider>,
    document.getElementById('root'),
  );
  ​
  ---------------------------------------------------------------------------------------
  ​
  新建store文件夹,并添加index.js
  ​
  import { init } from '@rematch/core';
  import { routerReducer as routing } from 'react-router-redux';
  import models from '../models';
  ​
  const store = init({
    models,
    redux: {
      reducers: {
        routing,
      },
    },
  });
  ​
  export default store;
  ​
  ---------------------------------------------------------------------------------------
  ​
  新建models文件夹,并添加index
  ​
  models结构
  ├── common
  │   ├── bizLineList.js
  │   └── index.js
  └── index.js复制代码

bug

Redux DevTools 要升级到最新版,2.16.0有bug

参考

从新思考Redux

Rematch: 从新设计 Redux

精读《从新思考 Redux》

相关文章
相关标签/搜索