【React hooks】关于useCallback带来的闭包问题实践方案

问题描述

// 举个栗子,我用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

据我所知,目前至少还有两种解决方案,刚和隔壁大佬讨论完,还没来得及研究,周末研究一下,后续完善!