• rematch:当你受不了redux繁琐写法的时候,是时候了解一波rematch了


    前言:

    前段时间学习完react后,刚好就接到公司一个react项目的迭代,顺便巩固一下前段时间的学习成果。项目使用的是redux+react-router,将所有的数据都放在redux中,异步处理数据使用redux-saga。由于是迭代项目,所以代码风格还是沿用之前项目的写法,将所有的数据都放在redux中。写redux的时候心中默默的吐槽了无数次,特别是每次从服务端异步获取数据的时候心中都会默默的想 “我只是想实现从服务端取一个列表数据,然后保存在store中,为什么需要引入redux-saga,并且引入后写法还是很繁琐(虽然流程很清晰明了,但是需要进行大量的复制粘贴)” ,写着写着感觉心态就快崩了 。后面经过同事的介绍,了解到有rematch这么一个更好用的js状态容器,终于可以脱离redux了。

    简介:

    先看看rematch的官方介绍:

    Rematch是没有boilerplate的Redux最佳实践。没有多余的action types,action creators,switch 语句或者thunks。

    rematch是在redux的基础上再次封装后成果,在rematch中我们不用声明action类型、action创建函数、thuks、store配置、mapDispatchToProps、saga。如果你习惯使用vuex,那么你一定会喜欢上rematch的,因为rematch的写法和vuex基本一样。

    一个简单的demo:

    先看看我们的项目结构:

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

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

    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);
     

         

    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
                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踩了才知道。

  • 相关阅读:
    curl命令详解
    Linux 下 set env export declare浅浅 set和shopt命令详解--(shell定制) (转载)
    ps aux指令詳解
    smb设置参考手册 --详细参数
    Ajax
    JSON浅谈
    Date类型
    笔记本电脑不能上网的问题
    editplus 初步设置
    w10 系统升级
  • 原文地址:https://www.cnblogs.com/heavenYJJ/p/9363259.html
Copyright © 2020-2023  润新知