useEffect监听回调中的闭包问题

368 阅读1分钟

最近在项目中遇到一个棘手的问题,使用useState获取到的一直是初始值;下面用一个例子来说明问题的产生和解决的方案

import React, { useState, useEffect, useRef, useCallback } from "react";
import { useEventListener, useMemoizedFn } from "ahooks";

export default () => {
const [value, setValue] = useState(0);
const ref = useRef(null);

const getValue2 = () => {
    // 获取不到外部的数据 一直为初始值
    console.log("getValue2", value);
};

const getValue = () => {
    // 获取不到外部的数据 一直为初始值
    console.log("getValue", value);
    getValue2();
};

useEffect(() => {
    document.addEventListener("scroll", getValue);
    return () => {
        document.removeEventListener("scroll", getValue);
    };
}, []);

useEventListener(
"click",
() => {
    setValue((v) => v + 1);
},
{ target: ref }
);

return (
<div style={{ height: 1000 }}>
<button ref={ref} type="button">
You click {value} times
</button>
</div>
);
};

简单描述下:使用useEffect来监听scroll触发事件,打印点击的次数,会发现两次的打印都为初始值:0;即如果用useEffect来监听事件,其中依赖为空,会导致监听事件里的回调获取不到外部的数据;

解决方案

方案一:通过useRef做一层转换

const getValue = () => {
    console.log("getValue", value);
    getValue2();
};
const ref 2= useRef(getValue)
ref2.current = getValue

useEffect(() => {
    const getValueNew= () => ref2.current()
    document.addEventListener("scroll", getValueNew);
    return () => {
        document.removeEventListener("scroll", getValueNew);
    };
}, []);

方案二:使用ahooks的useMemoizedFn

const getValue = useMemoizedFn(() => {
    console.log("getValue", value);
    getValue2();
});