[리액트] 06. 컴포넌트 반복

PYOUNANI·2022년 11월 6일
0

React

목록 보기
5/9
post-thumbnail

6. 컴포넌트 반복

6-1. 컴포넌트 반복

웹 애플리케이션을 만들다 보면 코드를 반복하여 사용할 때가 있다. 반복적인 코드를 효율적으로 보여주고 관리하는 방법을 배워보겠다.


6-1-1. map() 함수

자바스크립트 배열 객체의 내장 함수인 map함수를 이용해볼 수 있다. map 함수는 배열의 각 요소를 원하는 규칙에 따라 변환한 후, 그 결과로 새로운 배열을 생성한다.

arr.map(callback, [thisArg])
  • arr : 변환할 배열
  • callback : arr 배열의 요소를 변환시킬 함수
  • thisArg(선택 항목): callback 함수 내부에서 사용할 this 레퍼런스
var numbers = [1, 2, 3, 4, 5];

var processed = numbers.map(function(num){
  return num * num;
  
// var processed = numbers.map(num => num*num);

console.log(processed);

map함수를 이용하여 각 요소를 제곱할 수 있는 함수를 넣어 processed라는 새로운 배열을 만들어냈다.

6-1-2. 데이터 배열을 컴포넌트 배열로 변환하기

문자열로 구성된 name 배열을 만들어 map함수를 이용하여 nameList라는 새로운 배열을 만들어낸다.

// IterationSample.js

const IterationSample = () => {
  const names = ['눈사람', '얼음', '눈', '바람'];
  const nameList = names.map(name => <li>{name}</li>);
  return <ul>{nameList}</ul>;
};

export default IterationSample;

App 컴포넌트에서 IterationSample 컴포넌트를 불러와 렌더링한다.

// App.js

import { Component } from 'react';
import IterationSample from './IterationSample';

class App extends Component {
  render() {
    return (
      <IterationSample />
    );
  }
}

export default App;

웹브라우저를 확인하면 원하는 대로 렌더링이 되었으나 크롬 개발자 도구의 콘솔을 확인하면 "key" prop이 없다는 경고 메시지가 뜬다.

6. 컴포넌트 반복

6-2. key

리액트에서 key는 컴포넌트 배열을 렌더링했을 때 어떤 원소에 변동이 있었는지 알아내기 위해 사용이 된다. 물론 key가 없다면 Virtual DOM을 비교하는 과정에서 리스트를 순차적으로 비교할 것이다. 라지만 key가 있다면 어떤 변화가 일어났는지 빠르게 알아낼 수 있다.


6-2-1. key 설정

key값을 설정할 때는 map 함수의 인자로 전달되는 함수 내부에서 컴포넌트 props를 설정하듯이 설정하면 된다. key의 값은 언제나 유일해야 하기 때문에 각 데이터의 고윳값을 key값으로 사용하는 것이 적절하다.

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

하지만 key의 없는 경우도 있다. 앞서 봤던 예제가 그 예이다. 이럴떈 index을 이용해볼 수 있다.

// IterationSample.js

const IterationSample = () => {
 const names = ['눈사람', '얼음', '눈', '바람'];
 const nameList = names.map((name, index) => <li key={index}>{name}</li>);
 return <ul>{nameList}</ul>;
};

export default IterationSample;

단 데이터별로 고유한 값이 없을 때만 index을 활용해야 한다.
index을 key로 이용하게 되면 배열이 변경될 때 효율적으로 리렌더링하지 못하기 때문이다.

6. 컴포넌트 반복

6-3. 응용

지금까지는 고정된 배열을 렌더링 했다. 이제는 동적인 배열을 렌더링을 구현하는 것을 알아보겠다.


6-3-1. 초기 상태 설정하기

// IterationSample.js

import {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); // 새로운 항목을 추가할 때 사용할 id

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

export default IterationSample;

6-3-2. 데이터 추가 기능 구현하기

새로운 이름을 등록할 수 있는 기능을 구현해보자.

ul 태그 상단에 input과 button을 렌더링하고, input 상태를 관리한다.

// IterationSample.js

import {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); // 새로운 항목을 추가할 때 사용할 id

  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;

버튼을 클릭했을 때 호출할 onClick 함수를 선언해 버튼의 onClick 이벤트에 설정한다.

// IterationSample.js

import {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);  // 새로운 항목을 추가할 때 사용할 id

  const onChange = e => setInputText(e.target.value);
  const onClick = () => {
    const nextNames = names.concat({
      id: nextId, //nextId 값을 id로 설정하고
      text: inputText
    });
    setNextId(nextId + 1);  // nextId 값에 1을 더해준다.
    setNames(nextNames);  // names 값을 업데이트한다.
    setInputText('');  //inputText를 비운다.
  };

  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;

코드를 보면 배열에 추가할 때 push가 아닌 concat를 사용한 것을 볼 수 있는데, push는 기존 배열 자체를 변경해주는 반면 concat는 새로운 배열로 바꿔주기에 concat을 사용했다.

리액트에서 컴포넌트의 성능을 최적화하기 위해서는 어떤 값을 업데이트를 할 때 기존 상태를 두면서 새로운 값의 상태로 설정해야 한다. 이를 불변성 유지라고 한다. 그 이유는 뒤애서 다뤄보겠다.

6-3-3. 데이터 제거 기능 구현하기

각 항목을 더블클릭했을 때 해당 항목이 화면에서 사라지는 기능을 구현해보자.
위에서 concat를 사용한 것처럼 불변성을 유지해야 하기에 이번에도 배열의 내장 함수인 filter를 사용하여 구현하겠다.

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);  // 새로운 항목을 추가할 때 사용할 id

  const onChange = e => setInputText(e.target.value);
  const onClick = () => {
    const nextNames = names.concat({
      id: nextId,  //nextId 값을 id로 설정하고
      text: inputText
    });
    setNextId(nextId + 1);  // nextId 값에 1을 더해준다.
    setNames(nextNames);  // names 값을 업데이트한다.
    setInputText('');  //inputText를 비운다.
  };
  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;

0개의 댓글