我的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])
);
}
我不明白为什么playingTrackInd总是-1只有在handleKeyUp函数。在其他任何地方,包括返回语句,它都是正确的值。谢谢。
问题在于下面的效果:
useEffect(() => {
document.addEventListener('keyup', handleKeyUp);
}, []);
可以看到,您只注册了一次handleKeyUp
(在挂载时触发一次Effect,因为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