1

分享我在项目中做的一个取消请求小需求,能学到一点东西~

 1 year ago
source link: https://www.fly63.com/article/detial/11676
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

我们公司所做的产品,包含了很多的表单,同时也存在了很多表单提交的按钮

我们现在有这样的一个需求:每一处提交按钮,都需要做取消提交的功能,也就是:按钮在loading的过程中支持取消提交

其实这个功能并不难,但是由于我们项目用了两套请求的封装,所以让我有点措手不及。

前面说了,我们项目现阶段使用了两套请求封装:

  • 第一套:基于ahook2的useRequest
  • 第二套:基于umi-request的dispatch-action

方案一:Map存储

所以想要统一做取消请求的处理,只能找找这两套请求的共同点,诶,终于让我找到了,这两套共用的一个地方:请求响应拦截器

我的方案比较简单:

  • 定义一个Map
  • 每次提交后,发送请求,在请求拦截器里存储此接口的信息
  • 当再次点击按钮执行取消提交时,从Map中找到此接口,并取消它
  • 每次响应拦截器里都要及时从Map中清除此接口信息,保证不影响以后的请求

而怎么取消请求呢?由于两套请求都是基于fetch封装的,想要取消它,得使用:AbortController

具体用法可以看MDN

629eeb240485b.jpg

大概的伪代码如下

// request.ts

// 用来存储接口
export const map = new Map()

request.请求拦截器((options) => {
  const { url, method } = options
  const c = new AbortController()
  map.set(`${url}${method}`, c)
  options.signal = c.signal

return options
})

request.响应拦截器((options) => {
  const { url, method } = options
  // 每次有响应都要清除,不影响后面请求
  map.delete(`${url}${method}`)
})
// Button.tsx

import { map } from 'request.ts'

// 提交
const submit = () => {
  request.get('api/v1/request', {
    success: () => {},
    fail: () => {}
  })
}

// 取消提交
const cancelSubmit = () => {
  // 获取controller实例
  const c = map.get(`api/v1/requestget`)
  // 执行取消请求
  c.abort()
}

return <button onClick={submit} onCancel={cancelSubmit}>提交</button>

这样做弊端太多了,且不说会和取消重复请求这个功能冲突(因为取消重复请求功能也需要用到Map的存储)

我举个例子,如果一个页面中有两处地方发送了同请求方法同请求路径的请求,那么我想取消其中一个请求,你要怎么保证取消的准确性呢?

所以此方案直接废弃

方案二:封装cancel方法

前面说了,本项目有两套请求:

  • 基于ahooks2的useRequest
  • 基于umi-request的dispatch-action

由于ahooks2并没有提供现成的cancel方法(ahooks3是有的),所以就得自己实现了

具体伪代码如下

// useRequest.ts

import { useRequest } from 'ahooks'
import { useRef } from 'react'

export const (server, options) => {
  const c = useRef(null)
  const { run, ...rest } = useRequest(server, 
  {
    manual: true,
    ...ooptions
  })

const cancel = () => {
    c.current?.abort()
  }

const fn = (...args) => {
    c.current = new AbortController()
    run(...args, {
      signal: c.current.signal
    })
  }

return {
    ...rest,
    run: fn,
    cancel
  }
}
// Button.tsx

import { useRequest } from 'useRequest'

const server = (data, options) => {
  return request.get('url', {
    data,
    ...options
  })
}

const { run, cancel }=useRequest(server)

return <button onClick={run} onCancel={cancel}>提交</button>

dispatch-action怎么办?

刚刚说了我们用了两套请求,但是我刚刚只写了useRequest的cancel方法,没有写dispatch-action的

嘿嘿,因为我把项目中所有使用dispatch-action的提交按钮,全部改成使用useRequest了,进行了统一~

作者:林三心不学挖掘机 
来源:前端之神

链接: https://www.fly63.com/article/detial/11676


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK