反应功能不使用更新状态

react function not using updated state

我的 handleKeyUp 函数没有使用状态中的任何新值。在函数中,playingTrackInd 始终等于 -1,即使 playingTrackInd 的状态已更新为其他内容。

import React, { useEffect, useRef, useState } from 'react';
import { Container } from 'react-bootstrap';

export interface Track {
    artist: string,
    title: string,
    uri: string,
    albumUrl: string,
    previewUrl?: string,
}

export default function Dashboard() {
    const [playingTrackInd, setPlayingTrackInd] = useState<number>(-1);
    const tracks = useRef<Array<Track>>([]);
   
    // add keylistener
    useEffect(() => {
        document.addEventListener('keyup', handleKeyUp);

    }, []);

    // this function always has playingTrackInd as -1, even when the state has been updated by setPlayingTrackInd
    function handleKeyUp(e: any) {
        if (e.keyCode === 39) {
            // right arrow pressed
            if (playingTrackInd === tracks.current.length - 1) {
                getRandomTrack(() => setPlayingTrackInd(prevInd => prevInd + 1));
            }
            else {
                setPlayingTrackInd(prevInd => prevInd + 1)
            }

        }
        else if (e.keyCode === 37) { // left arrrow key pressed
            if (playingTrackInd > 0) {
                setPlayingTrackInd(prevInd => prevInd - 1);
            }
        }
    }

    // function that gets random track and adds it to tracks
    function getRandomTrack(callback?: Function) {
        getRandomTrackFromApi().then((track) => {
            tracks.current.push(track);
            if (callback) {
                callback();
            }
        })
    }

    // get first random track and set playingTrackInd to 0. This happens as soon as the component is loaded
    useEffect(() => {
        if (playingTrackInd === -1) { 
            getRandomTrack(() => setPlayingTrackInd(0));
        }
        getRandomTrack();
        getRandomTrack();


        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, []);

    return (
        (component that depends on tracks[playingTrackInd])
    );
}

我不明白为什么只有在handleKeyUp函数中playingTrackInd总是-1。在其他任何地方,包括 return 语句,它都是正确的值。谢谢

问题出在下面的效果上:

    useEffect(() => {
        document.addEventListener('keyup', handleKeyUp);
    }, []);

如你所见,你注册了一次handleKeyUp(效果被触发一次,挂载时,因为deps数组是空的)。 handleKeyUp 通过闭包 (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures) 记住了 playingTrackInd 的最后一个值。 playingTrackInd 更新时,组件重新呈现,创建新的 handleKeyUp 函数,但您已经注册了记住旧值的 handleKeyUp,并且您没有更新该侦听器。

您可以解决该问题,例如将 handleKeyUp 传递给依赖项数组,如下所示:

    useEffect(() => {
        document.addEventListener('keyup', handleKeyUp);
        return () => {
          document.removeEventListener('keyup', handleKeyUp);
        }
    }, [handleKeyUp]);

在注册新的侦听器之前不要忘记删除侦听器。解决方案并不理想,因为每次渲染都会创建 handleKeyUp 函数。您的第二个 useEffect 挂钩中存在同样的问题。

我强烈建议将您的函数包装到 useCallback (https://reactjs.org/docs/hooks-reference.html#usecallback) 中,并在 deps 数组中传递所有依赖项。这样你的功能就不会在每次渲染时都创建,而只会在它们的依赖关系发生变化时才创建。

另一件事是有一个 eslint 规则可以帮助您跟踪 deps 数组中所有必需的 deps,检查 exhaustive-deps https://reactjs.org/docs/hooks-rules.html