尽管标题和艺术家道具的 useEffect 发生了变化,但 React 状态并未在 DOM 中更新

问题描述 投票:0回答:1

我正在 React 中构建一个音乐播放器,当道具

title
artist
发生变化时,我尝试使用
useEffect
动态更新曲目
title
artist
。然而,虽然值在
console.log()
中正确更新,但它们不会反映在 DOM 中。

我正在为

useState
title
使用状态钩子
artist
,并且我确保当这些状态发生变化时组件应该重新渲染。尽管如此,DOM 仍然不会更新为新值。

import React, { useEffect, useState } from 'react';
import { FaBackward, FaPlay, FaPause, FaForward, FaHeart, FaRandom, FaVolumeUp } from 'react-icons/fa';
import useAudio from '../Audio/useAudio';
import api from '../../Services/api';


const Playbar = ({ url, title, artist, id }) => {


    //url is the audio url
    //title is the title of the track
    //artist is the artist of the song
    //id is the id of the track
    const [playing, toggle] = useAudio(url);
    
    const [titles, setTitles] = useState(title || "");
    const [artists, setArtists] = useState(artist || "");

    // Update titles and artists when props change
    useEffect(() => {
        setTitles(title || "");
        setArtists(artist || "");
    }, [title, artist]);


    // Update current playing track in the database if playing is true and the track has changed
    useEffect(() => {
        if (playing) {
            api.post(`/api/currentplaying`, {
                playing: playing,
                title: titles,
                artist: artists,
                id: id
            });
        }
    }, [titles, artists, playing, id]);

    // Fetch current playing track from the database

    useEffect(() => {
        const fetchCurrentPlaying = async () => {
            try {
                const res = await api.get(`/api/fetchcurrentplaying/${id}`);
                if (res.data.current && res.data.current.length > 0) {
                    const currentTrack = res.data.current[0];
                    setTitles(currentTrack.title);
                    setArtists(currentTrack.artist);
                } else {
                    console.log("No current playing data found.");
                }
            } catch (error) {
                console.error('Error fetching current playing:', error);
            }
        };

        fetchCurrentPlaying();
    }, [id]);


    // Log when the playbar is updated
    useEffect(() => {
        console.log("Playbar updated with:", { url, title, artist });
    }, [url, title, artist]);

    return (
        <footer className="fixed text-white bottom-0 left-0 right-0 bg-gray-800 p-2 flex items-center">
            <div className="flex items-center">
                <img src="https://placehold.co/50x50" alt="Current song" className="rounded-full mr-4" />
                <div>
                    <p className="font-bold">{titles || "No Title"}</p>
                    {console.log("Title", titles)}
                    {console.log("Artist", artists)}
                    <p>{artists || "No Artist"}</p>
                </div>
            </div>
            <div className="flex-1 flex justify-center items-center">
                <FaBackward className="mx-4" />
                {playing ? (
                    <FaPause className="mx-4" onClick={toggle} />
                ) : (
                    <FaPlay className="mx-4 " onClick={toggle} />
                )}
                <FaForward className="mx-4" />
            </div>
            <div className="flex items-center">
                <FaHeart className="mx-4" />
                <FaRandom className="mx-4" />
                <FaVolumeUp className="mx-4" />
            </div>
        </footer>
    );
}

export default Playbar;

问题:

title
artist
属性被传递到 Playbar 组件中,并且状态变量标题和艺术家在
useEffect
内相应更新。 这些状态变量正确记录在
console.log()
中,但 DOM 未反映更新后的状态值。 我已经确认状态正在控制台中更新,但它没有按预期重新渲染 DOM。

我尝试过的:

我使用

useEffect
在 props 更改时更新状态值。 我确保正确调用
setTitles
setArtists
。 我在 Playbar 组件中使用 React.memo,但问题仍然存在。

任何人都可以帮助我确定为什么更新的状态值没有反映在 DOM 中,即使它们已正确记录?

使用Audio.js

import { useState, useEffect } from 'react';

const useAudio = (url) => {
    const [audio, setAudio] = useState(new Audio(url));
    const [playing, setPlaying] = useState(false);

    const toggle = () => {
        setPlaying(prev => !prev);
    };

    useEffect(() => {
        const handleEnded = () => setPlaying(false);
        audio.addEventListener('ended', handleEnded);

        return () => {
            audio.pause();
            audio.removeEventListener('ended', handleEnded);
        };
    }, [audio]);

    useEffect(() => {
        if (url && url !== undefined) {
            const newAudio = new Audio(url);
            setAudio(newAudio);
            audio.src = url;
            setPlaying(true);
            
        }else{
            setPlaying(false);
        }
    }, [url]);

    useEffect(() => {
        if (playing && url) {            
            audio.play().catch(error => {
                console.error("Error playing audio:", error);
            });
        } else {
            audio.pause();
        }
    }, [playing, audio]);

    return [playing, toggle];
};

export default useAudio;
javascript reactjs react-native react-hooks
1个回答
0
投票

请查看以下代码。

...

useEffect(() => {
   setTitles(title || "");
   setArtists(artist || "");
}, [title, artist]);

useEffect(() => {
   if (playing) {
      api.post(`/api/currentplaying`, {
         playing: playing,
         title: titles,
         artist: artists,
         id: id
    });
    }
}, [titles, artists, playing, id]);
...

可能出现的故障

如果每当标题或艺术家更改时 id 或playing 也发生更改,则第二个 useEffect 挂钩将更新数据库中的过时的标题或状态艺术家或两者

失败原因

失败的原因是,React 将所有 useEffect 句柄合并为一个 useEffect 并执行它。代码的顺序将相对于源代码。每个处理程序的适用性将与依赖项数组相关。

因此,如果每当标题或艺术家改变时,playing 或 id 也改变,那么最终的 useEffect 将如下所示。 它是单个 useEffect 处理程序。当它执行时,状态设置器 setTitles 或 setArtists 会将状态更改排队并触发新的渲染。然而,正如我们所知,只有当当前运行的处理程序完全结束时,这个新的渲染才会开始。因此 React 也会继续运行 post 查询。由于处理程序中标题和艺术家的状态值始终保留为上次渲染的快照,它是陈旧的。因此,新值将意外地不会更新到数据库中。

...

useEffect(() => {
   setTitles(title || "");
   setArtists(artist || "");

   if (playing) {
      api.post(`/api/currentplaying`, {
         playing: playing,
         title: titles,
         artist: artists,
         id: id
    });

}, [title, artist, playing, id]);

...

一个可能的解决方案

根据组件中收到的相应道具,可能不需要以下两个状态定义。因此,不需要的 props 可以被删除,并直接引用相应的 props 来代替它。此更改也将消除通过 useEffect 重置的需要。您也许可以阅读这里提倡的这种代码重构根据 props 或 state 更新状态

const [titles, setTitles] = useState(title || "");
const [artists, setArtists] = useState(artist || "");

另一种解决方案

如果你想保留状态,也可以。但是,请使用按键而不是 useEffect 重置状态。使用密钥可能是确保正确重置相关状态的最佳方法。此处已对此进行了详细介绍当道具更改时重置所有状态

© www.soinside.com 2019 - 2024. All rights reserved.