React: 警告:列表中的每个子元素应该有一个唯一的 key。

14 浏览
0 Comments

React: 警告:列表中的每个子元素应该有一个唯一的 key。

我一直在调试这个问题,结果开始掉头发。到目前为止还没有找到解决方案。这是一个`Teaser`组件。最初我在为`Home`组件编写测试,但由于`styled-components`的错误,它有一些错误,所以我的技术负责人告诉我为这个新的`Teaser`组件编写测试(这是`Home`组件中的新组件),因为它可能会产生一些影响。当运行`Teaser.test.tsx`时,我会得到以下错误(与键相关):

FAIL  src/features/home/Teaser.test.tsx (6.781s)
  Teaser component
    × renders Teaser component when user has tonieboxes (185ms)
  ● Teaser component › renders Teaser component when user has tonieboxes
    expect(jest.fn()).not.toBeCalled()
    Expected number of calls: 0
    Received number of calls: 1
    1: "Warning: Each child in a list should have a unique \"key\" prop, "·
    Check the render method of `Teaser`.", "", "
        in Fragment (created by Teaser)
        in Teaser (at Teaser.test.tsx:16)
        in I18nextProvider (at test-utils/index.jsx:38)
        in AuthProvider (at test-utils/index.jsx:37)
        in ConfigProvider (at test-utils/index.jsx:36)
        in ThemeProvider (at test-utils/index.jsx:34)
        in Router (created by MemoryRouter)
        in MemoryRouter (at test-utils/index.jsx:33)
        in Providers"
      41 | // eslint-disable-next-line jest/no-duplicate-hooks
      42 | afterEach(() => {
    > 43 |   expect(console.error).not.toBeCalled()
         |                             ^
      44 |   expect(console.warn).not.toBeCalled()
      45 | 
      46 |   // Reset any request handlers that we may add during the tests,
      at Object. (src/setupTests.js:43:29)

我的`Teaser`测试代码:

import React from 'react'
import { render, screen } from '../../utils/test-utils'
import { Teaser, Tonieboxes } from './Teaser'
const tonieboxes: Tonieboxes[] = [
  {
    id: 'toniebox-id-1',
    name: 'toniebox-name-1',
    imageUrl: 'toniebox-image-1',
  },
]
describe('Teaser component', () => {
  const welcomeMessage = 'welcome-message'
  test('renders Teaser component when user has tonieboxes', () => {
    render()
    expect(screen.getByTestId(welcomeMessage)).toBeInTheDocument()
  })
})

我的`Teaser`组件:

import React, { useState, useEffect } from 'react'
import { useTranslation } from 'react-i18next'
import styled from 'styled-components'
import {
  variables,
  Text,
  Bello,
  media,
  Headline,
  Modal,
} from '@boxine/tonies-ui'
import { Link } from 'react-router-dom'
import { HorizontalScrollList } from '../../components/HorizontalScrollList/index'
import BenjaminBlümchen from '../../assets/01_Teaser_Charakter Benjamin.png'
import BibiAndTinaImg from '../../assets/05_Teaser_Charaktere Bibi&Tina.png'
/* German Images */
import newEpisodesImgDE from '../../assets/03_Teaser_Welcome Audiothek DE.png'
import newTonieBoxTurqouiseImgDE from '../../assets/02_2_Teaser_Toniebox Turquoise DE.png'
import creativeToniesImgDE from '../../assets/04_Teaser_Kreativ-Tonies DE.png'
import registerTonieboxImgDE from '../../assets/02_1_Teaser_Toniebox registrieren DE.png'
/* English Images */
import newEpisodesImg from '../../assets/03_Teaser_Welcome Audiothek EN.png'
import newTonieBoxTurqouiseImg from '../../assets/02_2_Teaser_Toniebox Turquoise EN.png'
import creativeToniesImg from '../../assets/04_Teaser_Kreativ-Tonies EN.png'
import AddTonieboxModalContent from '../tonieboxes-page/components/AddTonieboxModalContent'
import registerTonieboxImg from '../../assets/02_1_Teaser_Toniebox registrieren EN.png'
export interface Tonieboxes {
  id: string
  name: string
  imageUrl: string
}
interface TeaserProps {
  tonieboxes: Tonieboxes[]
}
interface TunesTeaser {
  alt: string
  src: string
  link: string
  noTonieboxes?: boolean
}
const tunesTeasersDE: TunesTeaser[] = [
  {
    alt: 'BenjaminBlümchen',
    src: BenjaminBlümchen,
    link: '/audio-library?filter=beee313f-55b2-40c1-8032-c41057f92e21',
  },
  {
    alt: 'Tonieboxen',
    src: newTonieBoxTurqouiseImgDE,
    link: '/tonieboxes',
  },
  {
    alt: '400 Neue Folgen',
    src: newEpisodesImgDE,
    link: '/audio-library',
  },
  {
    alt: 'Kreativ Tonies',
    src: creativeToniesImgDE,
    link: '/creative-tonies',
  },
  {
    alt: 'Bibi und Tina',
    src: BibiAndTinaImg,
    link: '/audio-library?filter=dacc4edb-ad1d-4ecd-b98c-b4b31983b5f8',
  },
]
const tunesTeasersNoTonieboxesDE: TunesTeaser[] = [
  {
    alt: 'BenjaminBlümchen',
    src: BenjaminBlümchen,
    link: '/audio-library?filter=beee313f-55b2-40c1-8032-c41057f92e21',
  },
  {
    alt: 'Registriere Deine Toniebox',
    src: registerTonieboxImgDE,
    link: '',
    noTonieboxes: true,
  },
  {
    alt: '400 Neue Folgen',
    src: newEpisodesImgDE,
    link: '/audio-library',
  },
  {
    alt: 'Kreativ Tonies',
    src: creativeToniesImgDE,
    link: '/creative-tonies',
  },
  {
    alt: 'Bibi und Tina',
    src: BibiAndTinaImg,
    link: '/audio-library?filter=dacc4edb-ad1d-4ecd-b98c-b4b31983b5f8',
  },
]
const tunesTeasersEng: TunesTeaser[] = [
  {
    alt: 'Benjamin Bluemchen',
    src: BenjaminBlümchen,
    link: '/audio-library?filter=beee313f-55b2-40c1-8032-c41057f92e21',
  },
  {
    alt: 'Tonieboxes',
    src: newTonieBoxTurqouiseImg,
    link: '/tonieboxes',
  },
  {
    alt: '400 New Episodes',
    src: newEpisodesImg,
    link: '/audio-library',
  },
  {
    alt: 'Creative Tonies',
    src: creativeToniesImg,
    link: '/creative-tonies',
  },
  {
    alt: 'Bibi and Tina',
    src: BibiAndTinaImg,
    link: '/audio-library?filter=dacc4edb-ad1d-4ecd-b98c-b4b31983b5f8',
  },
]
const tunesTeasersNoTonieboxesEng: TunesTeaser[] = [
  {
    alt: 'Benjamin Bluemchen',
    src: BenjaminBlümchen,
    link: '/audio-library?filter=beee313f-55b2-40c1-8032-c41057f92e21',
  },
  {
    alt: 'Register Your Toniebox',
    src: registerTonieboxImg,
    link: '',
    noTonieboxes: true,
  },
  {
    alt: '400 New Episodes',
    src: newEpisodesImg,
    link: '/audio-library',
  },
  {
    alt: 'Creative Tonies',
    src: creativeToniesImg,
    link: '/creative-tonies',
  },
  {
    alt: 'Bibi and Tina',
    src: BibiAndTinaImg,
    link: '/audio-library?filter=dacc4edb-ad1d-4ecd-b98c-b4b31983b5f8',
  },
]
const Wrapper = styled.div`
  margin: 1rem 0 0;
`
const StyledLink = styled(Link)`
  display: block;
`
const List = styled.li`
  display: block;
  cursor: pointer;
`
const StyledHeadline = styled(Headline)`
  text-align: center;
`
const StyledText = styled(Text)`
  text-align: center;
  ${media.tablet`
    font-size: 1rem;
  `}
  ${media.laptop`
    font-size: 1.25rem;
  `}
`
const TextWrapper = styled.div`
  position: relative;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  margin-bottom: 1rem;
  width: 18rem;
    ${media.mobileL`
    width: 21rem;
    `}
    ${media.tablet`
    width: 26rem;
    `}
    ${media.laptop`
    width: 27rem;
    `}
`
const StyledHorizontalScrollList = styled(HorizontalScrollList)`
  ul {
    padding: 0 1rem 0.5rem 0;
  }
`
const ScrollListWrapper = styled.div`
  margin-left: 1rem;
  ${media.laptopL`
    margin-left: 0;
  `}
`
export const TeaserCard = styled.img`
  width: 100%;
  height: 100%;
  border-radius: 1rem;
  box-shadow: 0.25rem 0.25rem 0 0 ${props => props.theme.DirtyWhiteDarker};
  ${media.tablet`
    box-shadow: 0.375rem 0.375rem 0 0 ${props => props.theme.DirtyWhiteDarker};
  `}
  ${media.laptop`
    box-shadow: 0.5rem 0.5rem 0 0 ${props => props.theme.DirtyWhiteDarker};
  `}
`
export function Teaser({ tonieboxes }: TeaserProps) {
  const [columns, setColumns] = useState(3)
  const [toggleTonieboxModal, setToggleTonieboxModal] = useState(false)
  const [allBoxes, setAllBoxes] = useState(tonieboxes)
  const [tunesTeasers, setTunesTeasers] = useState([])
  const { i18n } = useTranslation()
  const { t } = useTranslation(['home'])
  function toggleModal() {
    setToggleTonieboxModal(!toggleTonieboxModal)
  }
  function tonieboxAdded(toniebox) {
    setAllBoxes([...allBoxes, toniebox])
  }
  useEffect(() => {
    function update() {
      const matchTablet = window.matchMedia(
        `(min-width: ${variables.screenTablet}px)`
      ).matches
      const matchScreenMobileLarge = window.matchMedia(
        `(min-width: ${variables.screenMobileL}px)`
      ).matches
      setColumns(matchTablet ? 2.75 : matchScreenMobileLarge ? 2 : 1.35)
    }
    update()
    function checkAndSetTunesTeasers() {
      if (i18n.language === 'de') {
        if (tonieboxes.length === 0) {
          setTunesTeasers(tunesTeasersNoTonieboxesDE)
        } else {
          setTunesTeasers(tunesTeasersDE)
        }
      } else {
        if (tonieboxes.length === 0) {
          setTunesTeasers(tunesTeasersNoTonieboxesEng)
        } else {
          setTunesTeasers(tunesTeasersEng)
        }
      }
    }
    checkAndSetTunesTeasers()
    window.addEventListener('resize', update)
    return () => window.removeEventListener('resize', update)
  }, [i18n.language, tonieboxes.length])
  return (
    <>
      
        
          
            Werde ein Ipsum der Tonies
          
          
            Bist du bereit für Hörabenteuer? Entdecke jetzt die ganze Vielfalt
            der Tonies.
          
        
        
          
            {tunesTeasers.map(teaser => {
              return (
                <>
                  {teaser.noTonieboxes ? (
                    
                      
                    
                  ) : (
                    
                      
                    
                  )}
                
              )
            })}
          
        
      
      
        
      
    
  )
}

0
0 Comments

在上述代码中,出现了一个React警告:“每个列表中的子元素应该有一个唯一的key”。这个警告的原因是在渲染列表时,没有为每个子元素提供一个唯一的key属性。

解决这个问题的方法是为每个子元素(在这里是<List><StyledLink>)提供一个唯一的key属性。根据代码中的逻辑,可以使用teaser.alt作为key属性的值。

下面是修改后的代码:

<StyledHorizontalScrollList columns={columns}>
  {tunesTeasers.map(teaser => teaser.noTonieboxes ? (
    <List key={teaser.alt} onClick={toggleModal}>
      <TeaserCard alt={teaser.alt} src={teaser.src} />
    </List>
  ) : (
    <StyledLink key={teaser.alt} to={teaser.link}>
      <TeaserCard alt={teaser.alt} src={teaser.src} />
    </StyledLink>
  ))}
</StyledHorizontalScrollList>

通过为每个子元素提供一个唯一的key属性,我们可以解决React警告“每个列表中的子元素应该有一个唯一的key”的问题。

0
0 Comments

React:警告:列表中的每个子元素应该有一个唯一的key。

这个警告出现的原因是,React要求在使用map函数映射元素时,每个子元素都需要有一个唯一的key。在上述代码中,警告可能与tunesTeasers.map有关。需要在最外层元素中添加一个React key,即在这种情况下是Fragment。根据代码,可以看出teaser.link在集合中是唯一的,但如果不是唯一的,可能需要为元素提供一个唯一的id属性作为React key。

解决方法是在映射的最外层元素中添加一个唯一的key属性。根据上述代码,可以将key属性添加到Fragment、List和StyledLink元素中。

下面是修改后的代码:

{tunesTeasers.map(teaser => {
  return (
    <Fragment key={teaser.link}>
      {teaser.noTonieboxes ? (
        <List key={teaser.alt} onClick={toggleModal}>
          <TeaserCard src={teaser.src} alt={teaser.alt} />
        </List>
      ) : (
        <StyledLink key={teaser.alt} to={teaser.link}>
          <TeaserCard src={teaser.src} alt={teaser.alt} />
        </StyledLink>
      )}
    </Fragment>
  )
})}

通过在每个子元素中添加唯一的key属性,警告将不再出现。感谢您的阅读!

0