🧑💻 寫在開頭
點贊 + 收藏 === 學會🤣🤣🤣
前言
還記得上週我們團隊在招聘前端工程師,一個看起來經驗豐富的候選人坐在我對面。
"你們項目中是如何處理按鈕重複點擊的問題的?"我拋出了這個看似簡單的問題。
"這個簡單,使用防抖就可以了。"他很快回答。
然而,當我繼續追問細節時,他卻陷入了沉思...
實際上,這個問題看似簡單,但是要真正的解決好,需要考慮很多細節。在我面試了很多候選人中,能完整答出來的不到20%。
問題背景
在日常開發中,我們經常會遇到這樣的場景:
- 用户瘋狂點擊提交按鈕
- 表單重複提交導致數據異常
- 批量操作按鈕被連續觸發
這些問題如果處理不當,輕則影響用户體驗,重則可能造成數據錯誤。今天,就讓我們通過一個真實的面試場景,逐步深入這個問題
面試場景
面試官:項目中如何處理按鈕重複點擊的問題?
候選人:可以使用防抖(debounce)。
const debouncedSubmit = debounce(submit, 300);
面試官:那假設我防抖設置了1秒,我現在請求了,但是接口響應比較慢,要3秒,用户在這3秒內點擊了多次,那怎麼辦? 防抖是不是就沒用了?
一般説到這裏,很多人就不知道怎麼搞了
候選人:可以給按鈕加上 loading 狀態,點擊後設置 loading 為 true,等操作完成後再設置為 false。
const [loading, setLoading] = useState(false);
const handleSubmit = async () => {
setLoading(true);
try {
await submitData();
} finally {
setLoading(false);
}
}
面試官:這個思路不錯,但是如果項目中有很多按鈕都需要這樣處理,你會怎麼做?
候選人:額...每個按鈕都寫一遍 loading 狀態管理?
面試官:那樣就會有很多重複代碼,有沒有想過怎麼封裝呢?
到這裏也卡掉了很多人
解決方案
我們可以封裝一個自定義 Hook
import {useState,useCallback,useRef} from 'react'
function useLock(asyncFn) {
const [loading, setLoading] = useState(false)
const asyncFnRef = useRef(null)
asyncFnRef.current = asyncFn
const run = useCallback(async (...args) => {
if(loading) return
setLoading(true)
try {
await asyncFnRef.current(...args)
} finally {
setLoading(false)
}
}, [loading])
return [loading,run]
}
然後封裝一個通用的 Button 組件
import {Button as AntButton} from 'antd'
const Button = ({onClick,...props})=>{
const {loading, run} = useLock(onClick || (()=> {}))
return <AntButton loading={loading} {...props} onClick={run}></button>
}
使用示例
const Demo = () => {
const handleSubmit = async () => {
// 模擬異步請求
await new Promise(resolve => setTimeout(resolve, 2000))
console.log('提交成功')
}
return (
<Button onClick={handleSubmit}>
提交
</Button>
)
}
可以看到 在 handleSubmit 執行的時候 Button 會自動添加 loading, 在請求完成後 loading 會自動變為 false。
方案優勢
- 零侵入性 :使用方式與普通按鈕完全一致
- 自動處理 :自動管理 loading 狀態,無需手動控制
希望這篇文章對你有幫助!如果覺得有用,別忘了點個贊 👍
討論
你在項目中是如何處理按鈕重複點擊的問題的?歡迎在評論區分享你的解決方案!