反应:警告列表中的每个孩子都应该有一个唯一的钥匙 [英] React: Warning each child in a list should have a unique key

查看:68
本文介绍了反应:警告列表中的每个孩子都应该有一个唯一的钥匙的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在调试并开始脱发.到目前为止,我还没有找到解决方案.这是Teaser组件.我最初是为Home组件编写测试,但由于组件的样式而有一些错误,所以我的技术负责人告诉我要为此新的Teaser组件(这是Home组件中的新组件)编写测试. ),因为它可能正在发挥作用.运行Teaser.test.tsx时,出现此错误(与键有关):

I have been debugging this and started losing hair. So far I haven't found a solution yet. This the Teaser component. I was initally writing tests for Home component, but it had some errors because of styled-components, so my techlead told me to write tests for this new Teaser component (which is the component that was new in Home component), because maybe it is having some effect. When running the Teaser.test.tsx I get this error (related to keys):

 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.<anonymous> (src/setupTests.js:43:29)

我的预告片测试:


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(<Teaser tonieboxes={tonieboxes} />)
    expect(screen.getByTestId(welcomeMessage)).toBeInTheDocument()
  })
})


我的预告片组件:


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[]>(tonieboxes)
  const [tunesTeasers, setTunesTeasers] = useState<TunesTeaser[]>([])

  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 (
    <>
      <Wrapper>
        <TextWrapper>
          <StyledHeadline
            styleTag={columns === 1.25 ? 'h3' : 'h2'}
            dataTestId="welcome-message"
          >
            Werde ein <Bello>Ipsum</Bello> der Tonies
          </StyledHeadline>
          <StyledText>
            Bist du bereit für Hörabenteuer? Entdecke jetzt die ganze Vielfalt
            der Tonies.
          </StyledText>
        </TextWrapper>
        <ScrollListWrapper>
          <StyledHorizontalScrollList columns={columns}>
            {tunesTeasers.map(teaser => {
              return (
                <>
                  {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>
                  )}
                </>
              )
            })}
          </StyledHorizontalScrollList>
        </ScrollListWrapper>
      </Wrapper>
      <Modal
        headline={t('add-toniebox-modal:AddTonieboxModalTitle')}
        isOpen={toggleTonieboxModal}
        onClose={toggleModal}
      >
        <AddTonieboxModalContent
          onClose={toggleModal}
          onSuccess={tonieboxAdded}
        />
      </Modal>
    </>
  )
}

推荐答案

这绝对是一个数组键问题,但似乎每个数据集(数组)中都有唯一的alt属性.

It's definitely an array key issue, but it seems you have unique alt attributes in each data set (array).

<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>

这篇关于反应:警告列表中的每个孩子都应该有一个唯一的钥匙的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆