위와 같은 네이버 웹툰 페이지에서는 카드를 컴포넌트로 만들어 활용할 수 있습니다.
그런데 이를 렌더링하는 경우
export default function PreveiwCardParent(){
return (
<>
<PreveiwCard imgSrc={""} title={""} subtitle={""} author={""}>
<PreveiwCard imgSrc={""} title={""} subtitle={""} author={""}>
<PreveiwCard imgSrc={""} title={""} subtitle={""} author={""}>
<PreveiwCard imgSrc={""} title={""} subtitle={""} author={""}>
<PreveiwCard imgSrc={""} title={""} subtitle={""} author={""}>
<PreveiwCard imgSrc={""} title={""} subtitle={""} author={""}>
</>
)
}
위와 같이 각각의 컴포넌트에 하나씩 데이터를 넘겨주는 것은 너무 비효율적입니다. 이 때 Array.prototype.map을 활용하면 보다 효율적으로 코드를 작성 할 수 있습니다.
return (
<>
{cardInfo.map(info => ({
<PreveiwCard
imgSrc={info.imgSrc}
title={info.title}
subtitle={info.subtitle}
author={info.author}
/>
}))}
</>
)
그런데 화면에는 잘 렌더링이 되지만, 콘솔창에 에러가 뜨는 것을 확인할 수 있습니다. Key는 엘레멘트에 안정적인 고유성을 부여하기 위해 사용하며 props를 넘길 때 배열 내부의 엘리먼트를 key로 지정해줘야합니다.
const listItems = number.map((list)=>
<li key={list.id} >
{list.text}
</li>
)
또한 key는 해당항목을 고유하게 식별할 수 있는 문자열을 사용하는 것이 좋습니다.
key가 왜필요할까?
- 리액트에서는 DOM을 효율적으로 업데이트하기 위해 setState를 할 때 비동기적으로 동작하고, 연속적으로 setState를 호출했을 때 배치처리를 하게 됩니다.
- 만약 map을 사용하는 경우 리액트에서는 컴포넌트가 리렌더링 될 때마다 map을 돌며, 이전에 렌더링 된 요소들과 비교하며 어떤 요소가 변경되었는지 파악하게 됩니다.
만약, 랜덤하게 뽑은 카드 배열을 자식 컴포넌트에서 하나씩 호출하고 싶은 경우 어떻게 해야할까?
import './App.css';
import React, {useState, useEffect} from 'react';
import BusinessCard from './components/BusinessCard';
import datas from './components/card'
function App() {
const [cards, setCards] = useState([])
const [selectcards, setSelectCards] = useState([])
//componentDidmount와 같이 동작
useEffect(() => {
setCards(datas)
}, [])
//random함수
function draw() {
//조건추가
if(selectcards.length > 2) {
const names = selectcards.reduce((acc, cur) => {
return acc = acc.concat(`${cur.name}, `)
}, "")
return alert(`당첨자는 ${names} 입니다.`)
}
const randomIdx = Math.floor(Math.random() * cards.length)
const randomItem = cards[randomIdx]
setCards(cards.filter(card => card.phoneNumber !== randomItem.phoneNumber))
setSelectCards([...selectcards, randomItem])
}
return (
<div>
{cards.length > 0 && <button onClick={draw}>추첨하기</button>}
{selectcards.length>0 && <BusinessCard selectcards={selectcards} key={selectcards.phoneNumber} />}
</div>
)
}
export default App;
import React from 'react'
function BusinessCard({selectcards}) {
return (
<>
{selectcards.map(card => {
return (
<div>
<div>회사: {card.company}</div>
<div>팀: {card.team}</div>
<div>이름: {card.name}</div>
<div>휴대번호: {card.phoneNumber}</div>
<div>이메일: {card.email}</div>
</div>
)
})}
</>
)
}
export default BusinessCard
return (
<div>
{ cards.length > 0 && <button onClick={draw}>추첨하기</button> }
{/* {pickedCards.length > 0 && (
<BusinessCard info={pickedCards[pickedCards.length-1]}/>
)} */}
{pickedCards.length > 0 && pickedCards.map(pickedCard => <BusinessCard info={pickedCard} key={pickedCard.phoneNumber} />)}
</div>
변수로 뺼수도 있습니다.
const result = pickedCards.map(pickedCard => <BusinessCard info={pickedCard} key={pickedCard.phoneNumber} />)
return (
<div>
{ cards.length > 0 && <button onClick={draw}>추첨하기</button> }
{/* {pickedCards.length > 0 && (
<BusinessCard info={pickedCards[pickedCards.length-1]}/>
)} */}
{pickedCards.length > 0 && result }
</div>
);
}