React之防止按鈕多次點擊事件?重復(fù)提交
React防止按鈕多次點擊事件 重復(fù)提交
為了方便,簡單的記錄一下:
在state中設(shè)置一個控制點擊事件
this.state = {
isClick: true
}在點擊事件的函數(shù)里
handleOk = () => {
const { isClick } = this.state
if (isClick) { //如果為true 開始執(zhí)行
this.setState({ isClick: false }) //將isClick 變成false,將不會執(zhí)行處理事件
// 編寫點擊事件執(zhí)行的代碼
const that = this // 為定時器中的setState綁定this
setTimeout(function () { // 設(shè)置延遲事件,1秒后將執(zhí)行
that.setState({ isClick: true }) // 將isClick設(shè)置為true
}, 1000);
}
};防止提交按鈕重復(fù)點擊的實踐
防止按鈕重復(fù)點擊
按鈕是前端界面中承接著用戶操作的很重要的一個環(huán)節(jié),前端界面用戶和系統(tǒng)的交互都通過按鈕來完成,與系統(tǒng)的交互自然就少不了把用戶的意向保存到系統(tǒng)中,如今面對前后端分離的部署方案,前端與后端的通信都是通過接口來完成的。
那么問題就來了發(fā)送一個接口就需要等待,那么等待的這段時間可長可短(根據(jù)用戶當(dāng)前的網(wǎng)絡(luò)時間決定的),如果一個請求三秒以后才回來,用戶在這一段時間再次點擊怎么辦。
在如今這個網(wǎng)速很快的時代,可能延遲是非常低的,所以給用戶考慮的時間并不多,但是如果請求時間長重復(fù)點擊就需要做限制了。
本次就是基于項目中的發(fā)送請求的按鈕做防止重復(fù)點擊的一些探索。
現(xiàn)在的前端的項目中發(fā)送請求大都采用 async/await 的語法糖吧異步請求封裝,正是因為是異步請求,回調(diào)之前的按鈕都是不應(yīng)該點擊的,這樣可以防止一些請求二次發(fā)送造成的一些bug。
舉一個簡單的例子,在項目中有一個按鈕是提交按鈕,把用戶的的一些信息通過調(diào)用接口的形式發(fā)送給后端,然后跳轉(zhuǎn)到詳情頁面,這就是一個很簡單的前端交互的場景。我們來簡單實現(xiàn)一下(基于React實現(xiàn)方式)
const ButtonClick: React.FC = () => {
const [value, setValue] = useState('');
const copyValue = useRef('');
const sendValue = (name: string): Promise<string> =>
new Promise((resolve: Function, reject: Function) => {
setTimeout(() => {
if (!name) {
return reject('請輸入對應(yīng)對容')
}
copyValue.current = name;
resolve('success');
}, 3000)
})
const getValue = async () => {
try {
const flag = await sendValue(value);
console.log(flag);
console.log(copyValue.current);
console.log('其他業(yè)務(wù)操作')
} catch (error) {
message.warning(error)
console.log(error)
}
}
const inputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setValue(event.target.value)
}
return (
<>
<Input onChange={inputChange} value={value} style = {{width: "300px"}}></Input>
<Button onClick={getValue} type = "primary" style = {{marginTop: "20px"}}>發(fā)送</Button>
</>
)
}
上面的代碼就是簡單把用戶輸入的內(nèi)容給持久化起來,這里借用了setTimeout來模仿網(wǎng)絡(luò)延遲的效果,如果在請求處理過程中不對重復(fù)點擊進行控制,那么就會出現(xiàn)下面的情況,連續(xù)發(fā)送好幾次請求,最終效果差強人意。

所以針對這個按鈕就需要做重復(fù)點擊控制,每次請求成功的時候才能恢復(fù)按鈕的可點擊狀態(tài)。
那么簡單的實現(xiàn)代碼就來了針對按鈕點擊的方法:
const [affectLoading,setAffectLoading] = useState(false);
const getValue = async () => {
if(affectLoading) {
return;
}
setAffectLoading(true);
try {
const flag = await sendValue(value).finally(() =>{
setAffectLoading(false)
})
console.log(flag);
console.log(copyValue.current);
console.log('其他業(yè)務(wù)操作')
} catch (error) {
message.warning(error)
console.log(error)
}
}
上面的方式針對按鈕事件單獨定義了一個變量進行了控制,每次都是請求完畢的時候把控制變量給置為false,上面這種方式確實是可行的但是如果針對每一個按鈕事件都需要單獨定義一個變量,會造成內(nèi)部的變量很多后期很難維護。那么就像節(jié)流一樣能不能針對這種情況抽離出一個公共的方法來實現(xiàn)呢。
經(jīng)過梳理,如果我們抽離出一個方法,和這種單獨的寫法是一致的,首先定義一個變量置為false,然后進行第一次請求,這個時候給變量置為true,等待請求結(jié)束再給變量置為false,這樣就達到了控制重復(fù)點擊的效果。前幾天解除了js的裝飾器,首先想到的就是使用裝飾器在代碼編譯的時候給注入這一過程??梢詫τ赗eact Hook 是沒有類這一個概念的。所以對于裝飾器也用不了(但是對于React的class組件還是可以使用的。
下面會給出實現(xiàn)方式)。對于React Hook則可以使用高階函數(shù)的方式實現(xiàn),傳入一個方法,返回包裝過的方法,高階函數(shù)類似與下面的方式:
const demo = () => {
console.log('處理業(yè)務(wù)邏輯');
}
const warpButton = (buttonEvent:Function) => {
return () => {
console.log('begin');
buttonEvent();
console.log('end');
}
}
HTML:
<Button onClick={warpButton(demo)}>發(fā)送</Button>
經(jīng)過warpButton的包裝可以給注入的方法執(zhí)行一個額外的邏輯,那么我們實現(xiàn)的邏輯也就可以基于次來實現(xiàn)了。
下面是代碼:
const getValue = async () => {
try {
const flag = await sendValue(value)
console.log(flag);
console.log(copyValue.current);
console.log('其他業(yè)務(wù)操作')
} catch (error) {
message.warning(error)
console.log(error)
}
}
const wrapButton = (buttonEvent: Function, messageValue?: string) => {
let flag = false;
return async function () {
if (flag) {
messageValue && message.warning(messageValue);
return;
}
flag = true;
//@ts-ignore
await buttonEvent.apply(this, arguments).finally(() => {
flag = false;
})
}
}
HTML:
<Button onClick={wrapButton(getValue,'loading')} type="primary" style={{ marginTop: "20px" }}>發(fā)送</Button>
通過這個高階函數(shù)可以自動幫助我們在執(zhí)行請求的時候控制對應(yīng)的請求狀態(tài),這樣就能夠做到自動對我們注入的函數(shù)進行控制。同時可以根據(jù)傳入的提示信息進行提示。
對于公共方法還需要在考慮一下兼容性,如果這里傳入的就是一個普通的js方法這樣就報錯了,所以需要對傳入的方法進行判斷,增加兼容性:
代碼如下
const wrapButton = (buttonEvent: Function, messageValue?: string) => {
let flag = false;
return async function () {
if (flag) {
messageValue && message.warning(messageValue);
return;
}
flag = true;
if (buttonEvent.constructor.name === 'AsyncFunction') {
//@ts-ignore
await buttonEvent.apply(this, arguments).finally(() => {
flag = false
})
} else {
//@ts-ignore
buttonEvent.apply(this, arguments);
flag = false;
}
}
}
對與React Hook 中可以使用高階函數(shù)的方式可以實現(xiàn),對于之前的class 組件則是可以使用裝飾器了,不僅看上去美觀同時使用起來也是比較方便。
但是裝飾器只能用于類和類的屬性上,不能用于方法上,因為存在函數(shù)提升。
直接給出裝飾器代碼:
const lockButton = (value: string = 'loading') => {
return (target?: any, key?: any, desc?: any) => {
const fn = desc.value;
let flag = false;
desc.value = async function () {
if (flag) {
message.warning(value);
return;
}
flag = true;
console.log(fn.constructor.name === 'AsyncFunction');
if (fn.constructor.name === 'AsyncFunction') {
//@ts-ignore
await fn.apply(this, arguments).finally(() => {
flag = false;
})
} else {
fn.apply(this, arguments);
flag = false;
}
return target;
}
}
}
在class組件中的使用:
class ChekcButton1 extends Component<{}, {}> {
constructor(props: {}) {
super(props)
this.state = {
}
}
private getData = (timer: number): Promise<Number> =>
new Promise((resolve) => {
setTimeout(() => {
resolve(timer);
}, timer)
})
@lockButton('異步buttton請求中')
async getValue1() {
const value = await this.getData(5000);
console.log(value);
if (value > 500) {
console.log('判斷');
}
}
render() {
return (
<>
<div>
測試class組件的button裝飾器:
<div>
<Button onClick={this.getValue1.bind(this)} type="primary">測試button</Button>
</div>
</div>
</>
)
}
}
總結(jié)了以上兩種方式不管使用裝飾器或者是高階函數(shù)的方式都可以做到對按鈕的點擊進行控制,但是究其根本還是通過定義變量控制的,所以自己也可以在其他框架中進行探索。
在vue中的嘗試:同樣繪制一個基礎(chǔ)的頁面一個按鈕一個輸入框模擬發(fā)送請求:
<template>
<div id="app">
<el-input v-model="input" placeholder="請輸入內(nèi)容" class="inputVDom"></el-input>
<el-button type="primary" @click="getValue">按鈕</el-button>
</div>
</template>
<script>
import { buttonLock } from "../../util/util";
export default {
name: "buttonLock",
data() {
return {
input: "",
copyValue: "",
};
},
methods: {
getData: function (value) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (!value) {
this.$message({
message: "'this needs some value!'",
type: "warning",
});
reject("this needs some Value!");
return;
}
this.copyValue = value;
resolve("success");
}, 3000);
});
},
getValue: async function() {
try {
const value = await this.getData(this.input);
console.log(value);
console.log(this.input)
}catch(error) {
console.log(error);
}
},
},
};
</script>
同樣的情況出現(xiàn)了,請求返回之前重復(fù)點擊就會重復(fù)執(zhí)行。
同樣的方法使用高階函數(shù)給他包裝起來。
//utils類中方法
const buttonLock = (buttonEvent) => {
let flag = false;
console.log(buttonEvent)
return async function() {
if(flag) {
console.log('loading')
return;
}
flag = true;
await buttonEvent.apply(this,arguments).finally(() => {
flag = false;
})
}
}
//使用:
methods:{
getValue: buttonLock(async function() {
try {
const value = await this.getData(this.input);
console.log(value);
console.log(this.input)
}catch(error) {
console.log(error);
}
})
}
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
關(guān)于React狀態(tài)管理的三個規(guī)則總結(jié)
隨著 JavaScript 單頁應(yīng)用開發(fā)日趨復(fù)雜,JavaScript 需要管理比任何時候都要多的 state (狀態(tài)),這篇文章主要給大家介紹了關(guān)于React狀態(tài)管理的三個規(guī)則,需要的朋友可以參考下2021-07-07
詳解create-react-app 2.0版本如何啟用裝飾器語法
這篇文章主要介紹了詳解create-react-app 2.0版本如何啟用裝飾器語法,cra2.0時代如何啟用裝飾器語法呢? 我們依舊采用的是react-app-rewired, 通過劫持webpack cofig對象, 達到修改的目的2018-10-10
詳解webpack + react + react-router 如何實現(xiàn)懶加載
這篇文章主要介紹了詳解webpack + react + react-router 如何實現(xiàn)懶加載,具有一定的參考價值,感興趣的小伙伴們可以參考一下。2017-11-11
在React中使用React.createRef:更優(yōu)雅的DOM引用方式
React提供了多種方式來引用DOM元素,其中React.createRef()是一種更為現(xiàn)代、更優(yōu)雅的方式,在這篇文章中,我們將深入了解React.createRef()的應(yīng)用,以及它為開發(fā)者帶來的便利,感興趣的朋友一起看看吧2024-01-01
react native實現(xiàn)往服務(wù)器上傳網(wǎng)絡(luò)圖片的實例
下面小編就為大家?guī)硪黄猺eact native實現(xiàn)往服務(wù)器上傳網(wǎng)絡(luò)圖片的實例。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-08-08
Remix后臺開發(fā)之remix-antd-admin配置過程
這篇文章主要為大家介紹了Remix后臺開發(fā)之remix-antd-admin配置過程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-04-04
react組件封裝input框的防抖處理的項目實現(xiàn)
本文主要介紹了react組件封裝input框的防抖處理的項目實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04

