問題描述
// 舉個栗子,我用hooks 寫了這么一個組件
let Test = () => {
/** Search base infos */
const [searchID, setSearchID] = useState(0)
/** Search info action */
const onSearchInfos = useCallback(() => {
let fetchUrl = '/api/getSearchInfos'
let fetchParams = { searchID }
fetch(fetchUrl, {
method: 'POST',
body: JSON.stringify(fetchParams)
}).then(res => res.json()
).then(res => {
console.log(res)
})
}, [])
return (
<>
<button onClick={() => {setSearchID(searchID + 1)}} >button1</button>
<button onClick={() => {onSearchInfos()}}>button2</button>
</>
)
}
export default Test
上述寫了一個很簡單的偽代碼功能,大致就是,點擊button1按鈕,searchID的值加1,點擊button2發送一個請求。
開始描述問題:當我們點擊了四次button1,把searchID的值更改到了4,然后點擊button2,會發現,發送出去的請求,searhID的值是0。
問題分析
為什么會產生這種問題呢?因為我們使用useCallback將請求數據的回調方法onSearchInfos包裹了一層,並且第二參數我們傳遞了一個[],表示只在組件第一次創建的時候,這個回調函數被創建,從而去提升性能!
回顧下, 我上面說到了什么?
只在第一次組件創建的時候onSearchInfos被創建!第一次!
也就是說searchID拿到的值是第一次被創建的時候,傳入的值,形成了一個閉包。
解決方案1
interface IRef {
current: any
}
let Test = () => {
/** Search base infos */
const [searchID, setSearchID] = useState(0)
/** 解決閉包問題 */
const fetchRef: IRef = useRef() // hooks為我們提供的一個通用容器,里面有一個current屬性
fetchRef.current = { // 為current這個屬性添加一個searchID,每當searchID狀態變更的時候,Test都會進行重新渲染,從而current能拿到最新的值
searchID
}
/** Search info action */
const onSearchInfos = useCallback(() => {
let fetchUrl = '/api/getSearchInfos'
let fetchParams = { ...fetchRef.current } // 解構參數,這里拿到的是外層fetchRef的引用
fetch(fetchUrl, {
method: 'POST',
body: JSON.stringify(fetchParams)
}).then(res => res.json()
).then(res => {
console.log(res)
})
}, [])
return (
<>
<button onClick={() => {setSearchID(searchID + 1)}} >button1</button>
<button onClick={() => {onSearchInfos()}}>button2</button>
</>
)
}
export default Test
解決方案2
解決方案3
據我所知,目前至少還有兩種解決方案,剛和隔壁大佬討論完,還沒來得及研究,周末研究一下,后續完善!