React Hooks的依賴項

有感這篇文章👉函數式編程看React Hooks(二)事件綁定反作用深度剖析,做者寫得條理很清晰。react

在此再強調一遍: 爲何要用useCallback去緩存onMouseMove這個函數?由於該函數是一個useEffect的依賴項,並且它是個引用類型的值,因此每一次re-render都會是新的函數。假若該函數未發生變化,其實沒有必要每次re-render都生成新的,這也是useCallbak最主要的做用。編程

下面延伸一丟丟,仍是講hooks的依賴項。從我本身還原的部分HashRouter代碼講起:緩存

export default function HashRouter(props) {
    let locationState = null;

    const [location, setlocation] = useState({
        pathname: window.location.hash.slice(1) || "/",
        state: locationState
    });

    const handleHashChange = useCallback(() => {
        console.log('執行 useCallback裏面的函數') // 只要hashchange就會執行
        setlocation({
            ...location,
            pathname: window.location.hash.slice(1), // 新pathname的值並無依賴上一個pathname
            state: locationState
        })
    }, []); // 這裏並無添加依賴

    useEffect(() => {
        console.log('執行useEffect函數') // 只會打印一次
        window.addEventListener('hashchange', handleHashChange)
        return () => {
            window.removeEventListener('hashchange', handleHashChange)
        }
    }, []); // 這裏也沒有添加依賴

    const val = {
        location,
        history: {
                push: to => {
                    if (typeof to === 'object') {
                        let {
                            pathname,
                            state
                        } = to;
                        window.location.hash = pathname;
                        locationState = state;
                        
                    } else {
                        window.location.hash = to;
                    }
            }
        }
    }
    return <ReactRouterContext.Provider value={val}>
        {
            props.children
        }
    </ReactRouterContext.Provider>
}
複製代碼

請看代碼中的註釋部分。是的,我在上述useCallback和useEffect中都沒有添加依賴項。其實這是很差的,會形成疑惑和不解。ide

但另外一方面,它實現了緩存函數&&只綁定一次hashchange事件&&在hashchange時正常切換頁面的設計初衷:函數式編程

  • 實現緩存handleHashChange這個方法。它只在初次render的 時候生成,以後re-render都返回的是緩存的函數。爲何讓依賴項是空這麼設計呢?由於它確實沒有須要的依賴項。handleHashChange內部的setlocation方法的值,都不依賴useState返回的變量。
  • useEffect雖然只執行一次,可是隻須要在初次執行的時候給hashchange綁定函數便可,沒必要每次re-render都從新去綁定和解綁。

當我一開始對比文章開頭引用的文章和本身這段代碼時,也感到疑惑,再仔細去篩查,發現:在setlocation時,是從外部獲取新值的這個緣由促成的。因此纔可以實現依賴項爲空,且照樣能刷新的目的。函數

下面咱們引入一組對照組,就能夠發現確實如此:post

export default function HashRouter(props) {
    let locationState = null;

    const [location, setlocation] = useState({
        pathname: window.location.hash.slice(1) || "/",
        state: locationState
    });
    const [count, setcount] = useState({  // +++++ 新增這一段 +++++
        number: 0
    })

    const handleHashChange = useCallback(() => {
        setcount({
            number: count.number+1  // +++++ 新增這一段 +++++
        });
        setlocation({
            ...location,
            pathname: window.location.hash.slice(1),
            state: locationState
        })
    }, []);

    useEffect(() => {
        window.addEventListener('hashchange', handleHashChange)
        return () => {
            window.removeEventListener('hashchange', handleHashChange)
        }
    }, []);
    console.log('count--------', count);  // +++++ 新增這一段 +++++ //會發現count只會增長到1,由於react的比較是Object.is!無論是primitive仍是引用類型的值 
    console.log('location', location);   // +++++ 新增這一段 +++++
    // 這個之因此會變化,是由於它的計算,是從window.location.hash中設置新值的,而不是依賴前一個值
   
    const val = {
        location,
        history: {
                push: to => {
                    if (typeof to === 'object') {
                        let {
                            pathname,
                            state
                        } = to;
                        window.location.hash = pathname;
                        locationState = state;
                        
                    } else {
                        window.location.hash = to;
                    }
            }
        }
    }
    return <ReactRouterContext.Provider value={val}>
        {
            props.children
        }
    </ReactRouterContext.Provider>
}
複製代碼

再看看執行這段代碼的打印結果:spa

這個結果就和上面推薦的文章的結果相同了。設計

相關文章
相關標籤/搜索