警告:即使具有唯一键,列表中的每个子元素都应该有一个唯一的“key”属性 - React

10

我想渲染一个数组,但它一直提示警告:“列表中的每个子元素都应该有一个唯一的“key”属性”,即使它们有唯一的键,我的数组包含三个元素,但它甚至不能正确地呈现第三个数组,并且第三个li上的按钮由于某种原因也无法工作。

import React, { useEffect, useRef } from "react";
import fire from "./firebase";
import firebase from "firebase"
import { useState } from "react"
import Header from "./header"
import Nav from "./nav"
import { NavLink, Redirect } from "react-router-dom";
import AudioPlayer from "./audioplayer"   

const Music = ({ match }) => {
    const audioRef = useRef();
    const audioPlayer = audioRef.current
    const [audioSrc, setAudioSrc] = useState()
    const [musics, setMusics] = useState([])
    const [user, setUser] = useState(null)
    const [pfp, setPfp] = useState(null)
    const [audioTitle, setAudioTitle] = useState(null)
    const { params: { uID } } = match;
    var userName;
    var tracksTitle = [];
    useEffect(()=>{
        firebase.database().ref("users/"+uID+"/praivate/login credentials").on("value", (snapshot)=>{
            setUser(snapshot.val().userName)
            setUser(snapshot.val().userName)
        })
        firebase.database().ref("users/"+uID+"/public/profile/pic").on("value", (snapshot)=>{
            console.log(snapshot.val().pfpUrl)
            setPfp(snapshot.val().pfpUrl)
        })
    }, [])
    var music;

    //maybe the problem is over here somewhere
    useEffect(()=>{
        firebase.database().ref("users/"+uID+"/public/songs/").on("value", (snapshot)=>{
            //stores data in music.
            music = snapshot.val()
            //gets all title of the data
            Object.values(music).forEach((value)=>{  
                tracksTitle.push(value.title)//pushes titles to tracksTitle
            })
            setMusics(tracksTitle) //stores all track title in musics
        })
       
    }, [uID])

    const [progVal, setProgVal] = useState("0%")
    function update(event){
        let duration = (event.target.currentTime/event.target.duration)*100
        setProgVal(duration)
    }
    
    function play(event){
        const value = event.target.getAttribute("value")
        console.log(value)
        if(value){
            firebase.database().ref("public/songs/"+value).on("value", (snapshot)=>{
                setAudioSrc(snapshot.val().aduioURL)
            })
        }
        setAudioTitle(value)
        audioPlayer.play()
        
    }

    function playAudio(){
        audioPlayer.play()
    }

    

    function stop(event){
        audioPlayer.pause()
    }

   
    
    return(
        <>
            <Nav />
            <div id = "profile" className = "main">
                <audio id = "player" ref = {audioRef} src = {audioSrc} name = "audioplayer" onTimeUpdate = {update}>
                </audio>
                <div class = "profile-container">
                    <div className = "cover-pic">
                        <img src = "" />
                        <div className = "pfp">
                            <img src = {pfp} height = "100" width = "100"/>
                        </div>
                        <div className = "User-Name">
                            <h1>{user}</h1>
                        </div>
                    </div>
                    <div class = "tabsContainer">
                    <div class = "tabs-holder">
                    <NavLink to = {"/u/"+uID+"/music"}><button><span>Music</span></button></NavLink>
                        <button><span>Playlist</span></button>
                        <button><span>About</span></button>
                    </div>
                </div>
                </div>
               
                <div class = "music-content-container">
                    <div class = "music-box">
                        <div class = "user-musics">
                            <ul>
                                {musics && musics.map((name, i)=>{
                                    console.log()
                                    return(
                                        <>
                                            //it does not render the third array properly
                                            <li key = {i}><button onClick = {play} value = {name}>Play</button>{name}</li>
                                        </>
                                    )
                                })}
                            </ul>
                        </div>
                    </div>
                </div>
                
            </div>
            <AudioPlayer progVal = {progVal} stop = {stop} playAudio = {playAudio}/>
            <Header />
        </>
    )
    }  
export default Music
//

Can you guys please help me to figure out the problem, thank you in advance.

更新:我的Firebase数据如下:

enter image description here

以下代码将选择每个数据的标题。

useEffect(()=>{
        firebase.database().ref("users/"+uID+"/public/songs/").on("value", (snapshot)=>{
            //stores data in music.
            music = snapshot.val()
            console.log(music)
            //gets all title of the data
            Object.values(music).forEach((value)=>{ 
                 
                tracksTitle.push(value.title)//pushes titles to tracksTitle
            })
            setMusics(tracksTitle) //stores all track in musics
        })

在此输入图片描述

“Fikka Fikka”按钮无法正常工作,当我将鼠标悬停在其上时,它甚至不会将光标变为文本光标,而在 Ed Sheeran 和 One Direction 中则没有这个问题。


1
首先,您正在使用数组的索引作为键,这会导致问题。如果歌曲具有唯一名称,则可以使用名称本身作为键。其次,您使用的片段也存在问题。在这种情况下,使用片段几乎没有意义。 - Lars Holdaas
@Laras谢谢,我试过了,但是不知道为什么第三个渲染的项目没有被正确地渲染,我无法选择文本,也无法点击按钮。 - DjBillje Official
1
所以,如果我理解正确的话,“musics”只是一个标题数组?React键错误是否已使用此处提供的其中一种解决方案解决?您能描述一下第三个列表项不起作用的意思吗?它只是第三个吗?(第1、2、4、5等都能工作吗?)如果React密钥问题已解决,并且现在有了一些新的错误,那么我建议为新问题提出新问题。 - Drew Reese
1
在最后一张截图中,li元素的映射似乎完全按预期工作。看起来有某些东西(一个不可见的元素)覆盖了您的UI,不允许点击穿过。您是否也检查了DOM以查看屏幕上呈现了什么? - Drew Reese
@DrewReese 是的,我打开了检查元素,它显示所有内容都已正确渲染,拥有它们的值和类名。 - DjBillje Official
1
好的,列表元素本身没有分配类名,但我更多地是指UI中可能覆盖它们的任何其他东西,即一些不可见的、绝对定位的元素。 - Drew Reese
1个回答

6

React的key需要放在映射返回的最外层元素上。

{musics && musics.map((name, i)=>{
  return(
  <Fragment key={i}>
    //it does not render the third array properly
    <li>
      <button onClick={play} value={name}>Play</button>
      {name}
    </li>
  </Fragment>)
})}

注意,在这里为了做到这一点,React片段缩写是不兼容的,你必须使用Fragment组件。或者,由于只有一个元素,Fragment基本上是没用的,可以将其删除并将键放在li上。

{musics && musics.map((name, i)=>{
  return(
  <li key={i}>
    <button onClick={play} value={name}>Play</button>
    {name}
  </li>)
})}

1
这个解决方案也使用数组中的索引作为键,这在React中是强烈不建议的。 - Lars Holdaas
4
是的,感到气馁了,但并非完全禁止使用。它符合可接受使用的标准,即似乎没有对数组进行重新排序或添加/删除元素。据我所知,数组元素只是在切换播放状态。不过,OP的问题与此无关。 - Drew Reese
1
@DjBilljeOfficial 让我们开始消除。删除按钮后面的代码。<AudioPlayer><Header />也要删除,以防它们有一些奇怪的CSS阻止了它。 - k.s.
1
请在主要评论区进行调试讨论。 - Drew Reese
1
@k.s 哎呀我真是太蠢了,<AudioPlayer>的大小不允许我点击按钮,谢谢大家,也对不起大家。这是一个非常愚蠢的错误,哈哈。我移除了 <AudioPlayer>,然后它就好了。 - DjBillje Official
显示剩余8条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接