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

这个结果就和上面推荐的文章的结果相同了。设计

相关文章
相关标签/搜索