您好,登錄后才能下訂單哦!
這篇文章主要介紹“ahooks怎么解決用戶多次提交問題”的相關知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強,希望這篇“ahooks怎么解決用戶多次提交問題”文章能幫助大家解決問題。
試想一下,有這么一個場景,有一個表單,你可能多次提交,就很可能導致結果不正確。
解決這類問題的方法有很多,比如添加 loading,在第一次點擊之后就無法再次點擊。另外一種方法就是給請求異步函數(shù)添加上一個靜態(tài)鎖,防止并發(fā)產生。這就是 ahooks 的 useLockFn 做的事情。
useLockFn
用于給一個異步函數(shù)增加競態(tài)鎖,防止并發(fā)執(zhí)行。
它的源碼比較簡單,如下所示:
import { useRef, useCallback } from 'react'; // 用于給一個異步函數(shù)增加競態(tài)鎖,防止并發(fā)執(zhí)行。 function useLockFn<P extends any[] = any[], V extends any = any>(fn: (...args: P) => Promise<V>) { // 是否現(xiàn)在處于一個鎖中 const lockRef = useRef(false); // 返回的是增加了競態(tài)鎖的函數(shù) return useCallback( async (...args: P) => { // 判斷請求是否正在進行 if (lockRef.current) return; // 請求中 lockRef.current = true; try { // 執(zhí)行原有請求 const ret = await fn(...args); // 請求完成,狀態(tài)鎖設置為 false lockRef.current = false; return ret; } catch (e) { // 請求失敗,狀態(tài)鎖設置為 false lockRef.current = false; throw e; } }, [fn], ); } export default useLockFn;
可以看到,它的入參是異步函數(shù),返回的是一個增加了競態(tài)鎖的函數(shù)。通過 lockRef 做一個標識位,初始化的時候它的值為 false。當正在請求,則設置為 true,從而下次再調用這個函數(shù)的時候,就直接 return,不執(zhí)行原函數(shù),從而達到加鎖的目的。
雖然實用,但缺點很明顯,我需要給每一個需要添加競態(tài)鎖的請求異步函數(shù)都手動加一遍。那有沒有比較通用和方便的方法呢?
答案是可以通過 axios 自動取消重復請求。
對于原生的 XMLHttpRequest 對象發(fā)起的 HTTP 請求,可以調用 XMLHttpRequest 對象的 abort 方法。
那么我們項目中常用的 axios 呢?它其實底層也是用的 XMLHttpRequest 對象,它對外暴露取消請求的 API 是 CancelToken??梢允褂萌缦拢?/p>
const CancelToken = axios.CancelToken; const source = CancelToken.source(); axios.post('/user/12345', { name: 'gopal' }, { cancelToken: source.token }) source.cancel('Operation canceled by the user.'); // 取消請求,參數(shù)是可選的
另外一種使用的方法是調用 CancelToken 的構造函數(shù)來創(chuàng)建 CancelToken,具體使用如下:
const CancelToken = axios.CancelToken; let cancel; axios.get('/user/12345', { cancelToken: new CancelToken(function executor(c) { cancel = c; }) }); cancel(); // 取消請求
知道了如何取消請求,那怎么做到自動取消呢?答案是通過 axios 的攔截器。
請求攔截器:該類攔截器的作用是在請求發(fā)送前統(tǒng)一執(zhí)行某些操作,比如在請求頭中添加 token 相關的字段。
響應攔截器:該類攔截器的作用是在接收到服務器響應后統(tǒng)一執(zhí)行某些操作,比如發(fā)現(xiàn)響應狀態(tài)碼為 401 時,自動跳轉到登錄頁。
具體的做法如下:
第一步,定義幾個重要的輔助函數(shù)。
generateReqKey
:用于根據(jù)當前請求的信息,生成請求 Key。只有 key 相同才會判定為是重復請求。這一點很重要,而且可能跟具體的業(yè)務場景有關,比如有一種請求,輸入框模糊搜索,用戶高頻輸入關鍵字,一次性發(fā)出多個請求,可能先發(fā)出的請求,最后才響應,導致實際搜索結果與預期不符。這種其實就只需要根據(jù) URL 和請求方法判定其為重復請求,然后取消之前的請求就可以了。
這里我認為,如果有需要的話,可以暴露一個 API 給開發(fā)者進行自定義重復的規(guī)則。這里我們先根據(jù)請求方法、url、以及參數(shù)生成唯一的 key 去做。
function generateReqKey(config) { const { method, url, params, data } = config; return [method, url, Qs.stringify(params), Qs.stringify(data)].join("&"); }
addPendingRequest。用于把當前請求信息添加到 pendingRequest 對象中。
const pendingRequest = new Map(); function addPendingRequest(config) { const requestKey = generateReqKey(config); config.cancelToken = config.cancelToken || new axios.CancelToken((cancel) => { if (!pendingRequest.has(requestKey)) { pendingRequest.set(requestKey, cancel); } }); }
removePendingRequest。檢查是否存在重復請求,若存在則取消已發(fā)的請求。
function removePendingRequest(config) { const requestKey = generateReqKey(config); if (pendingRequest.has(requestKey)) { const cancelToken = pendingRequest.get(requestKey); cancelToken(requestKey); pendingRequest.delete(requestKey); } }
第二步,添加請求攔截器。
axios.interceptors.request.use( function (config) { removePendingRequest(config); // 檢查是否存在重復請求,若存在則取消已發(fā)的請求 addPendingRequest(config); // 把當前請求信息添加到pendingRequest對象中 return config; }, (error) => { return Promise.reject(error); } );
第二步,添加響應攔截器。
axios.interceptors.response.use( (response) => { removePendingRequest(response.config); // 從pendingRequest對象中移除請求 return response; }, (error) => { removePendingRequest(error.config || {}); // 從pendingRequest對象中移除請求 if (axios.isCancel(error)) { console.log("已取消的重復請求:" + error.message); } else { // 添加異常處理 } return Promise.reject(error); } );
到這一步,我們就通過 axios 完成了自動取消重復請求的功能。
關于“ahooks怎么解決用戶多次提交問題”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關的知識,可以關注億速云行業(yè)資訊頻道,小編每天都會為大家更新不同的知識點。
免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經查實,將立刻刪除涉嫌侵權內容。