从带有空依赖数组的React Hook中的eventListener访问值

时间:2019-10-29 22:26:30

标签: reactjs react-hooks

我正在尝试使用React挂钩来运行依赖于鼠标位置的画布动画。我在鼠标位置使用了一个自定义钩子,并编写了另一个自定义钩子来为画布设置动画。

this tutorial中提到并在React docs的注释中建议,在动画钩子中放置一个空的依赖数组可以防止它在鼠标移动时随时卸载和重新安装动画循环。 >

下面的代码可以正常工作,因为我可以在coords函数中访问drawNow(),但是每次鼠标移动时安装和卸载动画循环似乎都不是可接受的处理方式

React钩子中故意设置为没有依赖关系的一个访问事件侦听器是怎么来的?

这是动画和绘图功能。...

const drawNow = (context,coords) => {
    context.fillStyle = '#fff';
    context.beginPath();
    context.arc(coords.x,coords.y,50,0,2*Math.PI); // need coords here
    context.fill();
}

export const Canvas = () => {
    let ref = React.useRef();

    // custom hook that returns mouse position
    const coords = useMouseMove(); 

    React.useEffect(() => {
        let canvas = ref.current;
        let context = canvas.getContext('2d');

        const render = () => {
            aId = requestAnimationFrame(render);
            drawNow(context,coords); // requires current mouse coordinates
        };

        let aId = requestAnimationFrame(render);
        return () => cancelAnimationFrame(aId);
    }, [coords]); // dependancy array should be left blank so requestAnimationFrame mounts only once?

    return (
        <canvas ref={ref}/>
        style={{
            width: '100%',
            height: '100%',
        }}
    );
};

这是鼠标坐标的自定义钩子(引用此useEventListener

export const useMouseMove = () => {

    function getCoords(clientX,clientY) {
        return {
            x: clientX || 0,
            y: clientY || 0
        };
    }

    const [coords, setCoords] = useState(getCoords);

    useEventListener('mousemove', ({ clientX, clientY }) => {
        setCoords(getCoords(clientX,clientY));
    });
    return coords;
};

感谢,并希望进一步了解钩子和事件侦听器。

2 个答案:

答案 0 :(得分:0)

我认为您不需要为获取光标坐标而单独制作钩子,可以使用普通JavaScript来处理。

请查看此答案以了解操作方法:getting mouse coordinates in React and jQuery

答案 1 :(得分:0)

好的,我发现了我的问题。问题是useMouseMove()钩子正在用useState更新坐标,当我真的想使用useRef时,允许我imperatively“修改典型数据之外的孩子流量”,如here所述。

首先,我将useMouseMove()钩子与更通用的useEventListener结合使用,因此不必浏览不必要的抽象:

// a function that keeps track of mouse coordinates with useState()
export const useMouseMove = () => {

    function getCoords(clientX,clientY) {
        return {
            x: clientX || 0,
            y: clientY || 0
        };
    }

    const [coords, setCoords] = useState(getCoords);

    useEffect(
        () => {
            function handleMove(e) {
                setCoords(getCoords(e.clientX,e.clientY));
            }
            global.addEventListener('mousemove', handleMove);
            return () => {
                global.removeEventListener('mousemove', handleMove);
            };
        }
    );
    return coords;
};

下一步是将上述功能从useState()转换为useRef()

// a function that keeps track of mouse coordinates with useRef()
export const useMouseMove = () => {
    function getCoords(clientX,clientY) {
        return {
            x: clientX || 0,
            y: clientY || 0
        };
    }

    const coords = useRef(getCoords); // ref not state!

    useEffect(
        () => {
            function handleMove(e) {
                coords.current = getCoords(e.clientX,e.clientY);
            }
            global.addEventListener('mousemove', handleMove);
            return () => {
                global.removeEventListener('mousemove', handleMove);
            };
        }
    );
    return coords;
};

最后,我可以在动画循环内访问鼠标坐标,同时也使相关数组保持空白,从而防止每次鼠标移动时重新安装动画组件。

// the animation loop that mounts only once with mouse coordinates
export const Canvas = () => {
    let ref = React.useRef();

    // custom hook that returns mouse position
    const coords = useMouseMove();

    React.useEffect(() => {
        let canvas = ref.current;
        let context = canvas.getContext('2d');

        const render = () => {
            aId = requestAnimationFrame(render);
            drawNow(context,coords.current); // mouse coordinates from useRef()
        };

        let aId = requestAnimationFrame(render);
        return () => cancelAnimationFrame(aId);
    }, []); // dependancy array is blank, the animation loop mounts only once

    return (
        <canvas ref={ref}/>
        style={{
            width: '100%',
            height: '100%',
        }}
    );
};

由于有了这个逃生口,我能够在不重新安装动画循环的情况下创造无限的网络乐趣。