【问题标题】:Accessing values from an eventListener inside a React Hook with an empty dependancy array使用空依赖数组从 React Hook 中的事件侦听器访问值
【发布时间】:2020-02-25 05:07:17
【问题描述】:

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

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

所以下面的代码有效,因为我可以在 drawNow() 函数中访问 coords,但是每次鼠标移动时安装和卸载动画循环似乎不是一种可接受的做事方式。

如何访问特意设置为没有依赖关系的 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;
};

谢谢,期待更多地了解钩子和事件监听器。

【问题讨论】:

    标签: reactjs react-hooks


    【解决方案1】:

    好的,我发现了我的问题。问题是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%',
            }}
        );
    };
    

    多亏了这个逃生舱口,我能够在不重新安装动画循环的情况下创造无穷无尽的网络乐趣。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-06-12
      • 2019-08-09
      • 2014-01-10
      • 2019-07-07
      • 2018-01-11
      • 1970-01-01
      • 2021-11-12
      • 1970-01-01
      相关资源
      最近更新 更多