React: 警告:列表中的每个子元素应该有一个唯一的 key。
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 ToniesBist du bereit für Hörabenteuer? Entdecke jetzt die ganze Vielfalt der Tonies. {tunesTeasers.map(teaser => { return ( <> {teaser.noTonieboxes ? ( ) : (
)} > ) })} > ) }
在上述代码中,出现了一个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”的问题。
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属性,警告将不再出现。感谢您的阅读!