react函数没有使用更新状态



我的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-depshttps://reactjs.org/docs/hooks-rules.html

最新更新