redux的异步action和中间件

193 阅读5分钟

store整个流程是同步的,如果里面某个数据是异步获取到的,这时候action可以能已经跑到了store里了,这时候该怎么处理?

  • 使用以上方式创建的redux项目,它只有同步操作。每当dispatch action时,state会被立即更新。
  • "同步redux写法:action->reducer(state,action)->store(reducer)
  • ■action是一个普通的javascript对象、reducer是一个普通的方法,在reducer中根据当前的state、接收到的action:来生成一个新的state以达到更新状态的目的。那么问题来了,每次action被触发 (dispatch),reducer就会同步地对store:进行更新,在实际开发项目的时候,有很多需求都是需要通过接口等形式获取异步数据后再进行更新操作的,如何异步地对stoe进行更新呢?

使用中间件

  • RduX本身提供的Api与功能并不多,但是它提供了一个中间件(插件)机制,可以使用第三方提供的中间件或自己编写一个中间件来对Redux的功能进行增强。
  • dispatch一个action之后,到达reducer之前,进行一个额外的操作,就需要用到middleeware,你可以利用Redux middleware来进行日志记录、创建崩溃报告、调用异步接口或者路由等等。
  • 大多数异步操作都在action之后,reducer之前 zengqi

怎么增强的?

中间件其实就做了一件事——就是对store.dispatch()的增强。 其实就是把dispatch变成异步的操作,这样的话dispatch和请求数据都是异步的,它俩比较就是同步的。把分发action这个步骤变为异步的。

使用方法:

在创建store的时候引入中间件,在dispatch中使用。 常见的中间件:redux-thunk

redux-thunk

通过多参的柯里化函数以实现对函数的惰性求值,从而将同步的action转为异步的action。 它是封装了一个函数,来实现异步。他本身的封装就是个异步的。 安装:npm iredux-thunk -save-dev 导图thunk:import thunk from “redux-thunk” applyMiddleware()//该参数可以注入一个中间件,如果注入多个,就写成数组

store.dispatch()接收一个函数作为参数,然后由中间件来调用该回调函数,这个回调函数接收两个参数,dispatch(它是中间件传过来的,它并不是真正的store.dispatch,而是中间件的一个方法),getState(它用的机会也不多),dispatch是store传过来的,用这个dispatch直接分发action,语法:dispatch({type:"事件描述",payload:"异步请求回来的数据"}),其实这一步是把这个action对象传到了thunk函数里,再由thunk函数把异步请求的结果传给store。

thunk.png

import React ,{useState}from "react";
import store from "./store"

export default function App() {
    const [list,setList] = useState(store.getState())
    //监听store
    store.subscribe(()=>{
        console.log(store.getState())
        setList(store.getState())
    })
    //给按钮绑定点击事件
    const getCai=()=>{
        function cai(url,options){
        //这个是真正执行的函数,接收dispatch和getState两个参数
            return (dispatch,getState)=>{
                 fetch(url,options).then(req => 
                    {return req.json()}
                    ).then(result => 
                    //请求异步结果之后分发aciton
                        dispatch({
                            type:"result",
                            payload:result,
                        })
                        )
            }
        }
        //给dispatch传一个thunk函数
        store.dispatch(cai('http://www.qhdlink-student.top/',{
            method:"post",
            headers:{'content-type':'application/x-www-form-urlencoded'},
            body:携带参数'',
        }))
    }
  return (
    <div>
        <button onClick={getCai}>点击显示菜单</button>
        //渲染ui
        {
            Object.values(list).map(item => 
            <tr>{Object.entries(item).map(v => 
                v[0]==="path_coach" ? <td><img src={'http://www.qhdlink-student.top/'+v[1]} alt=""/>
                </td> : <td>{v[1]}</td>) }
            </tr>)
        }
    </div>
  )
}

redux-promise

  1. 它主要是封装了一个promise对象,在它里面做请求。
  2. 不同的中间件都有着自己的适用场景,react-thunk比较适合于简单的API请求的场景,而promise则更适合于输入输出的操作,通过判断返回的promise的状态返回新的action。
  3. 使用它store.dispatch()的参数其实是一个promise对象,因为async返回值就是promise。

点击按钮就向服务器添加数据,然后再请求回来放到store里,或者先把数据存到本地,在关闭浏览器的的时候,把数据存到服务器,根据需求去做事情。

promise.png

import React,{useState} from "react";
import store from "./store";

export default function App() {
    const [list,setList] = useState(store.getState());
    
    //监听store
    store.subscribe(()=>{
        console.log(store.getState())
        //改变list状态
        setList(store.getState())
    })
    //按钮绑定点击事件
    const getCai=()=>{
         //用async函数增强dispatch
         async function cai(url,options){
             let req = await fetch(url,options);
             let result = await req.json();
             if(result.err){
                return {
                    type:"err",
                    payload:result.err
                }
             }
             return {
                type:"result",
                payload:result,
             }
         }
         //在dispatch的时候先传一个promise对象
         store.dispatch(cai('http://www.qhdlink-student.top',{
            method:"post",
            headers:{'content-type':'application/x-www-form-urlencoded'},
            body:'请求时携带的参数',
        }))
    }
  return (
    <div>
        <button onClick={getCai}>点击显示菜单</button>
        //渲染ui
        {
            Object.values(list).map(item =>
            <tr>{Object.entries(item).map(v => 
                v[0]==="path_coach" ? <td><img src={'http://www.qhdlink-student.top/'+v[1]} alt=""/>
                </td>: <td>{v[1]}</td>) }
            </tr>)
        }
    </div>
  )
}

redux-thunk中间件可以让action创建函数先不返回一个action对象,而是返回一个函数,函数传递两个参数(dispatch,getState),在函数体内进行业务逻辑的封装

  1. 安装:npm install redux-hunk-save-dev
  2. 导入thunk:import thunk from 'redux-thunk
  3. 导入中间件:import (createStore,applyMiddleware} from'redux'
  4. 创建store:let store=createStore(reduceri函数,applyMiddleware(thunk)
  5. 直接将thunk中间件引入,放在applyMiddleware;方法之中,传入createStore方法,就完成了store.dispatch()的功能增强。即可以在reducer中进行一些异步的操作。
  6. applyMiddleware()它是Redux的原生方法,作用是将所有中间件组成一个数组,依次执行

redux-saga

  • 它是一个管理redux应用异步操作的中间件,用于代替redux-thunk的。它通过创建Sagea将所有异步操作逻辑存放在一个地方进行集中管理,以此将react种的同步操作与异步操作区分开来,以便于后期的管理与维护。
  • redux-saga相当于Redux原有数据流中多了一层,通过Action进行监听,从而捕获到监听的Action,然后派一个新的人去对state进行维护(这个看项目本身需求),通过更改state驱动view的变更。如下图所示

他们三个都可以实现异步的加载,只是实现原理不一样,即封装的时候不一样。