其他分享
首页 > 其他分享> > Redux学习(三)——redux-saga的使用、编写中间件函数、Reducer文件拆分

Redux学习(三)——redux-saga的使用、编写中间件函数、Reducer文件拆分

作者:互联网

一、redux-devtools

我们之前讲过,redux可以方便的让我们对状态进行跟踪和调试,那么如何做到呢?

在这里插入图片描述

安装该工具需要两步:

  1. 第一步:在对应的浏览器中安装相关的插件(比如Chrome浏览器扩展商店中搜索Redux DevTools即可,其他方法可以参考GitHub);
  2. 第二步:在redux中继承devtools的中间件;

在这里插入图片描述
index.js:

import {createStore, applyMiddleware, compose} from 'redux'
import reducer from "./reducer.js";
import thunkMiddleware from 'redux-thunk'

// composeEnhancers函数
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({trace: true}) || compose;

// 通过applyMiddleware来结合多个Middleware,返回一个storeEnhancer
const storeEnhancer = applyMiddleware(thunkMiddleware)
const store = createStore(reducer, composeEnhancers(storeEnhancer))

export default store;

二、generator

saga中间件使用了ES6的generator语法,所以我们有必须简单讲解一下:
注意:我这里并没有列出generator的所有用法,事实上它的用法非常的灵活,大家可以自行去学习一下。

我们按照如下步骤演示一下生成器的使用过程:
在JavaScript中编写一个普通的函数,进行调用会立即拿到这个函数的返回结果。

如果我们将这个函数编写成一个生成器函数。调用iterator的next函数,会销毁一次迭代器,并且返回一个yield的结果。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

研究一下foo生成器函数代码的执行顺序
generator和promise一起使用:
在这里插入图片描述

   // generator 和 Promise一起使用
    function* bar() {
        console.log(111)
        const result = yield new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve('Hello Generator')
            }, 3000)
        })
        console.log(result)
    }

    const it = bar()
    // console.log(it.next().value); // Promise对象
    it.next().value.then((res) => {
        it.next(res)
    })

三、redux-saga的使用

redux-saga是另一个比较常用在redux发送异步请求的中间件,它的使用更加的灵活。

Redux-saga的使用步骤如下

  1. 安装redux-saga
    yarn add redux-saga
  2. 集成redux-saga中间件
    导入创建中间件的函数;
    通过创建中间件的函数,创建中间件,并且放到applyMiddleware函数中;
    启动中间件的监听过程,并且传入要监听的saga;

store/index.js:

import {createStore, applyMiddleware, compose} from 'redux'
import reducer from "./reducer.js";
import thunkMiddleware from 'redux-thunk'
import createSagaMiddleware from 'redux-saga'
import saga from './saga'

// composeEnhancers函数
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({trace: true}) || compose;

// 通过applyMiddleware来结合多个Middleware,返回一个storeEnhancer
// 1.引入thunkMiddleware中间件(代码在上面)
// 2.创建sagaMiddleware中间件
const sagaMiddleware = createSagaMiddleware()

const storeEnhancer = applyMiddleware(thunkMiddleware, sagaMiddleware)
const store = createStore(reducer, composeEnhancers(storeEnhancer))

sagaMiddleware.run(saga) // saga为生成器函数

export default store;

在这里插入图片描述
3. saga.js文件的编写

takeEvery:可以传入多个监听的actionType,每一个都可以被执行(对应有一个takeLatest,会取消前面的)

put:在saga中派发action不再是通过dispatch,而是通过put;

all:可以在yield的时候put多个action;

在这里插入图片描述
store/saga.js:

import {takeEvery, put, all, takeLatest} from 'redux-saga/effects'
import axios from "axios";
import {FETCH_HOME_MULTIDATA} from "./constants";
import {changeBannersAction,changeRecommendsAction} from "./actionCreator";
function* fetchHomeMultidata(action) {
    const res = yield axios.get('http://123.207.32.32:8000/home/multidata')
    const banners = res.data.data.banner.list
    const recommends = res.data.data.recommend.list
    // yield put(changeBannersAction(banners))
    // yield put(changeRecommendsAction(recommends))
    yield all([
        yield put(changeBannersAction(banners)),
        yield put(changeRecommendsAction(recommends))
    ])
}
function* mySaga() {
    // takeEvery: 每个action都会被执行
    // yield takeEvery(FETCH_HOME_MULTIDATA, fetchHomeMultidata)
    // takeLatest:一次只能监听一个对应的action
    // yield takeLatest(FETCH_HOME_MULTIDATA, fetchHomeMultidata)
    yield all([
        yield takeEvery(FETCH_HOME_MULTIDATA, fetchHomeMultidata)
        // yield takeLatest(FETCH_HOME_MULTIDATA, fetchHomeMultidata)
    ])
}
export default mySaga

在这里插入图片描述
home.js:

import React, {PureComponent} from 'react';
import {connect} from "react-redux";
import {
    addAction,
    fetchHomeMultidataAction
}from "../store/actionCreator";

class Home extends PureComponent {
    componentDidMount() {
        this.props.getHomeMultidata()
    }

    render() {
        return (
            <div>
                <h1>Home</h1>
                <h3>当前计数:{this.props.counter}</h3>
                <button onClick={e => this.props.increment()}>+1</button>
                <button onClick={e => this.props.addNumber(5)}>+5</button>
            </div>
        );
    }
}
const mapStateToProps = state => ({
    counter: state.counter
})
const mapDispatchToProps = dispatch => ({
    increment() {
        dispatch(addAction(1));
    },
    addNumber(num) {
        dispatch(addAction(num));
    },
    getHomeMultidata() {
        dispatch(fetchHomeMultidataAction)
    }
})
export default connect(mapStateToProps, mapDispatchToProps)(Home);

在这里插入图片描述
在这里插入图片描述

四、打印日志需求

前面我们已经提过,中间件的目的是在redux中插入一些自己的操作:

如果没有中间件,我们是否可以实现类似的代码呢? 可以在派发的前后进行相关的打印。
但是这种方式缺陷非常明显:

是否有一种更优雅的方式来处理这样的相同逻辑呢?

但是这样的代码有一个非常大的缺陷:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

修改dispatch

事实上,我们可以利用一个hack一点的技术:Monkey Patching,利用它可以修改原有的程序逻辑;
我们对代码进行如下的修改:

当然,我们可以将它封装到一个模块中,只要调用这个模块中的函数,就可以对store进行这样的处理:
在这里插入图片描述

thunk需求

redux-thunk的作用:
我们知道redux中利用一个中间件redux-thunk可以让我们的dispatch不再只是处理对象,并且可以处理函数;那么redux-thunk中的基本实现过程是怎么样的呢?事实上非常的简单。

我们来看下面的代码:
我们又对dispatch进行转换,这个dispatch会判断传入的
在这里插入图片描述

合并中间件

单个调用某个函数来合并中间件并不是特别的方便,我们可以封装一个函数来实现所有的中间件合并:
在这里插入图片描述
我们来理解一下上面操作之后,代码的流程:
在这里插入图片描述
当然,真实的中间件实现起来会更加的灵活,这里我们仅仅做一个抛砖引玉,有兴趣可以参考redux合并中间件的源码流程。

五、Reducer代码拆分

我们先来理解一下,为什么这个函数叫reducer?
我们来看一下目前我们的reducer:

因此,我们可以对reducer进行拆分:

六、Reducer文件拆分

目前我们已经将不同的状态处理拆分到不同的reducer中,我们来思考:

在这里插入图片描述

七、combineReducers函数

目前我们合并的方式是通过每次调用reducer函数自己来返回一个新的对象。
事实上,redux给我们提供了一个combineReducers函数可以方便的让我们对多个reducer进行合并:
在这里插入图片描述
那么combineReducers是如何实现的呢?
事实上,它也是讲我们传入的reducers合并到一个对象中,最终返回一个combination的函数(相当于我们之前的reducer函数了);

在执行combination函数的过程中,它会通过判断前后返回的数据是否相同来决定返回之前的state还是新的state; 新的state会触发订阅者发生对应的刷新,而旧的state可以有效的组织订阅者发生刷新;

标签:函数,saga,reducer,中间件,dispatch,redux
来源: https://blog.csdn.net/weixin_44827418/article/details/122785009