상세 컨텐츠

본문 제목

[리액트 스타터 3] 6장. 컴포넌트 반복

22-23/22-23 리액트 스타터 3

by 케이비이 2022. 11. 10. 10:00

본문

728x90

 

코드를 작성하다보면 다음과 같이 반복되는 코드를 작성할 수도 있습니다.

import React from ‘react‘;
 
const IterationSample = () => {
  return (
    <ul>
      <li>떡볶이</li>
      <li>마라탕</li>
      <li>치킨</li>
      <li>초밥</li>
    </ul>
  );
};
 
export default IterationSample;

 

위의 예시는 li 태그의 반복입니다. 위의 예시는 간단하기에 그냥 li 태그를 반복해서 작성해도 큰 문제가 없지만, 간단한 반복에서 나아가 반복되는 코드들이 많아진다면 코드의 양은 늘어나고 파일의 용량도 증가해 쓸데없는 낭비가 발생할 것 입니다. 이번 6장에서는 반복적인 내용을 효율적으로 보여주고 관리하는 방법에 대해서 설명하도록 하겠습니다.

 

6.1 자바스크립트 배열의 map() 함수

자바스크립트에서는 배열 객체의 내장 함수인 map() 함수를 사용하여 반복되는 컴포넌트를 렌더링 할 수 있습니다.

map() 함수는 매개변수로 전달된 함수를 이용해 배열의 각 요소를 원하는 기준에 따라 변환한 후 새로운 배열을 생성합니다.

 

6.1.1문법

arr.map(callback, [thisArg])
  • callback: 새로운 배열의 요소를 생성하는 함수
  • --callback의 매개변수 3가지 currentValue, index, array
  • thisArg(선택항목): callback 함수 내부에서 사용할 this 레퍼런스

 

6.1.2 예제

map 함수를 이용하여 배열[1, 2, 3, 4, 5]의 각 요소에 2를 곱한 새로운 배열을 생성해보겠습니다.

var numbers = [1, 2, 3, 4, 5];

var processed = numbers.map(function(num){
  return num * 2;
});
 
console.log(processed);
const numbers = [1, 2, 3, 4, 5];
const result = numbers.map(num => num * 2);
console.log(result);

 

6.2데이터 배열을 컴포넌트 배열로 반환하기

기존에 있는 배열을 이용해 컴포넌트로 구성된 배열을 생성할 수도 있습니다.

 

6.2.1 컴포넌트 수정하기

import React from 'react';
 
const IterationSample = () => {
  const names = ['떡볶이', '마라탕', '치킨', '초밥'];
  const nameList = names.map(name => <li>{name}</li>);
  return <ul>{nameList}</ul>;
};
 
export default IterationSample;

위와 같이 컴포넌트를 작성하고 실행하면 아래와 같이 출력됩니다.

  • 떡볶이
  • 마라탕
  • 치킨
  • 초밥

하지만 이렇게 출력이 되더라도 크롬의 개발자 도구를 통해 콘솔을 보게 되면 "key"가 prop이 없다는 경고 메세지가 뜹니다.

 

6.3 Key

리엑트에서 key는 배열을 렌더링 했을때 어떤 원소에 변화가 있었는지 알아내는데 사용합니다.

key가 없으면 Virtual Dom을 비교하는 과정에서 리스트를 순차적으로 비교해 변화를 감지하지만, key가 있다면 이 값을 통해 어떤 변화가 발생했는지 빠르게 알아낼 수 있습니다.

 

6.3.1 key 설정

key 값의 설정은  map 함수의 인자로 전달되는 함수 내부에서 컴포넌트 props를 설정하듯이 설정하면 됩니다.

key값은 언제나 유일해야 하기 때문에 데이터가 가지는 고유의 값을 key 값으로 설정해줍니다.

const articleList = articles.map(article => (
  <Article
      title={article.title}
      writer={article.writer}
      key={article.id}
  />
);

위의 예시는 특정한 고유값이 있는 경우입니다. 지금 여기서는 article.id를 key 값으로 설정해서 사용하고 있습니다.

const IterationSample = () => {
  const names = ['떡볶이', '마라탕', '치킨', '초밥'];
  const nameList = names.map((name, index) => <li key={index}> {name} </li>);
  return <ul>{nameList}</ul>;
};

export default IterationSample;

 하지만 우리가 앞서 만들어왔던 IterationSample 컴포넌트의 경우 고유 번호가 없습니다, 이럴 경우에는 map 함수에 전달되는 콜백함수의 인자인 index 값을 사용합니다. 위의 예시에는 key값으로 index를 전달하고 있습니다.

하지만 이렇게 index를 이용한 key값 지정은 데이터에 고유한 값이 없을때만 사용해야 합니다. 그렇지 않으면 배열이 변경될 때마다 효율적으로 리렌더링 하지 못합니다.

 

6.4 응용

앞서 배운 내용을 바탕으로 동적인 배열을 렌더링 해보고, 효율적인 리렌더링을 위해 고유한 값을 만드는 방법에 대해서 알아보겠습니다.

 

6.4.1 초기 상태 설정하기

import React, {useState} from "react";

const IterationSample = () => {
  const[names, setNames] = useState([
    { id: 1, text: '떡볶이'},
    { id: 2, text: '마라탕'},
    { id: 3, text: '치킨'},
    { id: 4, text: '초밥'}
  ]);
  const[inputText, setInputText] = useState('');
  const [nextId, setNextId] = useState(5);

  const nameList = names.map(name => <li key={name.id}>{name.Text}</li>);
  return <ul>{nameList}</ul>;
};

export default IterationSample;

위는 IterationSample 컴포넌트를 수정한 코드입니다. 3가지 상태를 비구조화 할당으로 useState를 사용하여 설정하고 있습니다. 첫번째는 데이터 배열을 설정하고 두번째는 텍스트를 입력할 수 있는 input의 상태입니다. 마지막 세번째는 첫번째 데이터 배열에 새로운 항목을 추가할 경우 사용할 고유 id를 위한 상태입니다.

 

6.4.2 데이터 추가 기능 구현하기

새로운 음식 이름을 등록하는 기능을 추가해봅시다.

import React, { useState } from 'react';

const IterationSample = () => {
  const [names, setNames] = useState([
    { id: 1, text: '떡볶이' },
    { id: 2, text: '마라탕' },
    { id: 3, text: '치킨' },
    { id: 4, text: '초밥' }
  ]);
  const [inputText, setInputText] = useState('');
  const [nextId, setNextId] = useState(5);
 
  const onChange = e => setInputText(e.target.value);

const namesList = names.map(name => <li key={name.id}>{name.text}</li>);
  return (
    <>
      <input value={inputText} onChange={onChange} />
      <button>추가</button>
      <ul>{namesList}</ul>
    </>
  );
};

export default IterationSample;

위는 ul 태그 상단에 input과 button을 렌더링 했습니다.

import React, { useState } from 'react';

const IterationSample = () => {
  const [names, setNames] = useState([
    { id: 1, text: '떡볶이' },
    { id: 2, text: '마라탕' },
    { id: 3, text: '치킨' },
    { id: 4, text: '초밥' }
  ]);
  const [inputText, setInputText] = useState('');
  const [nextId, setNextId] = useState(5);

  const onChange = e => setInputText(e.target.value);
  const onClick = () => {
    const nextNames = names.concat({
      id: nextId,
      text: inputText
    });
    setNextId(nextId + 1);
    setNames(nextNames);
    setInputText('');
  };

const namesList = names.map(name => <li key={name.id}>{name.text}</li>);
  return (
    <>
      <input value={inputText} onChange={onChange} />
      <button onclick={onclick}>추가</button>
      <ul>{namesList}</ul>
    </>
  );
};

export default IterationSample;

위에서는 onclick을 정의하여 버튼을 클릭했을 때 onclick을 이벤트로 발생하게 했습니다. onclick에서는 배열의 내장 함수인 concat을 사용하여 새로운 항목을 추가한 배열을 만들고 setNames을 통해 상태를 업데이트해 줍니다.

concat은 배열에 새로운 항목을 추가할때 배열 자체를 새로 만듭니다. 리액트는 기존의 상태를 두면서 새로운 값을 상태로 설정하는 불변성 유지를  통해 리액트 컴포넌트 성능을 최적화할 수 있습니다.

button을 클릭할 때마다 id값은 +1이 되고 입력된 input의 내용은 자동으로 비워집니다. 

 

6.4.3 데이터 제거 기능 구현하기

앞선 코드에서 항목을 더블 클릭했을 때 항목이 화면에서 사라지는 기능을 구현해 보도록 하겠습니다.

이번에도 불변성을 유지하면서 업데이트하기 위해 배열의 내장 함수인 filter을 사용해 특정 항목을 지워보도록 하겠습니다.

 

먼저 filter의 사용법부터 간단하게 알아보겠습니다.

const numbers = [1,2,3,4,5,6];
const biggerThanThree = numbers.filter(number => number >3);
//결과: [4,5,6]

filter는 함수의 인자에 원하는 조건을 반환하는 함수를 넣어주어 분류할 수 있습니다.

이 함수를 응용하여 배열에서 특정 원소만 제외시키는 것도 가능합니다.

 

그럼 한번 IterationSample코드에 적용해봅시다.

import React, { useState } from 'react';

const IterationSample = () => {
  const [names, setNames] = useState([
    { id: 1, text: '떡볶이' },
    { id: 2, text: '마라탕' },
    { id: 3, text: '치킨' },
    { id: 4, text: '초밥' }
  ]);
  const [inputText, setInputText] = useState('');
  const [nextId, setNextId] = useState(5);

  const onChange = e => setInputText(e.target.value);
  const onClick = () => {
    const nextNames = names.concat({
      id: nextId,
      text: inputText
    });
    setNextId(nextId + 1);
    setNames(nextNames);
    setInputText('');
  };
  const onRemove = id =>{
    const nextNames = names.filter(name => name.id !== id);
    setNames(nextNames);
  }
const namesList = names.map(name => (
  <li key={name.id} onDoubleClick={()=> onRemove(name.id)}>
    {name.text}
  </li>
));
  return (
    <>
      <input value={inputText} onChange={onChange} />
      <button onclick={onclick}>추가</button>
      <ul>{namesList}</ul>
    </>
  );
};

export default IterationSample;

위의 onRemove에서 id값을 인자로 받고 filter함수 조건으로 그 id값을 제외하라고 했기 때문에 더블 클릭된 요소는 제외되는 것을 알 수 있습니다.


Corner React3

Editor: 니나노

728x90

관련글 더보기