My handleKeyUp function is not using any of the new values in the state. In the function, playingTrackInd is always equal to -1, even when the state has been updated for playingTrackInd to be something else.
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])
);
}
I can't understand why playingTrackInd is always -1 only in the handleKeyUp function. Everywhere else, including the return statement, it is the proper value. Thanks.
The problem lies in the effect below:
useEffect(() => {
document.addEventListener('keyup', handleKeyUp);
}, []);
As you can see, you're registering handleKeyUp
once (Effect is triggered once, on mount, because deps array is empty). handleKeyUp
remembers last value of playingTrackInd
via Closure ( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures ). When playingTrackInd
is updated, component rerenders, new handleKeyUp
function is created, but you've already registered your handleKeyUp
that remembers old value, and you're not updating that listener.
You can fix that issue by for example by passing handleKeyUp
to dependency array like below:
useEffect(() => {
document.addEventListener('keyup', handleKeyUp);
return () => {
document.removeEventListener('keyup', handleKeyUp);
}
}, [handleKeyUp]);
Don't forget to remove listener before new listener is going to be registered. Solution is not ideal, because handleKeyUp
function is created every render. Same issue exists in your second useEffect
hook.
I strongly recommend to wrap your functions into useCallback
( https://reactjs.org/docs/hooks-reference.html#usecallback ) and pass all dependencies in deps arrays. That way your fuctions won't be created every render, but only when their dependencies change.
Another thing is that there's an eslint rule that will help you track all required deps in deps arrays, check exhaustive-deps
https://reactjs.org/docs/hooks-rules.html
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.