简体   繁体   中英

react function not using updated state

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM