상세 컨텐츠

본문 제목

[리액트를 다루는 기술]6장 컴포넌트 반복

21-22/21-22 리액트 마스터

by 도리에몽 2021. 11. 1. 19:10

본문

728x90

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

🐳 자바스크립트 배열 객체의 내장 함수인 map함수를 사용하여 반복되는 컴포넌트를 렌더링 할 수 있다. map함수는 파라미터로 전달된 함수를 사용해서 배열 내 각 요소를 원하는 규칙에 따라 변환한 후 그 결과로 새로운 배열을 생성해준다.

1) 문법

arr.map(callback, [thisArg])

  • callback : 새로운 배열의 요소를 생성하는 함수로 파라미터는 다음 세 가지이다.
    • currentValue : 현재 처리하고 있는 요소
    • index : 현재 처리하고 있는 요소의 index 값
    • array : 현재 처리하고 있는 원본 배열
  • thisArg(선택항목) : callback 함수 내부에서 사용할 this 레퍼런스

❗ map 함수 사용해보기

const numbers = [ 1, 2, 3, 4, 5];
const result = numbers.map(num => num * num);
console.log(result);

결과 -> [1,4,9,16,25]

 

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

위와 같은 원리로 기존 배열로 컴포넌트로 구성된 배열을 생성할 수도 있다.
import React from 'react';

const IterationSample = () => {
    const names = ['눈사람', '얼음', '눈', '바람'];
		 //JSX코드로 된 배열을 새로 생성한 후 nameList에 담는다.
    const nameList = names.map(name => <li>{name}</li>)
    return <ul>{nameList}</ul>;
};

export default IterationSample;
import React, { Component } from ‘react‘;
import IterationSample from ‘./IterationSample‘;

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

export default App;

-> "key" prop이 없다는 경고 메시지를 표시했는데 이 key는 무엇일까

 

3. key

리액트에서 key는 컴포넌트 배열을 렌더링 했을 때 어떤 원소에 변동이 있었는지 알아내려고 사용한다. key가 없을 때에는 Virtual DOM을 비교하는 과정에서 리스트를 순차적으로 비교하면서 변화를 감지한다. 하지만 key가 있다면 이 값을 사용하여 어떤 변화가 일어났는지 더욱 빠르게 알아낼 수 있다.

1) key설정

  • key값을 설정할 때는 map함수의 인자로 전달되는 함수 내부에서 컴포넌트 props를 설정하듯이 설정하면 된다.
  • key값은 언제나 유일해야 한다.
  • 컴포넌트에 고유 번호가 없을 때에는 map함수에 전달되는 콜백 함수의 인수인 index 값을 사용하면 된다.
import React from 'react';

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

export default IterationSample;

-> 이제 오류가 나지 않는다.

 

4. 응용

앞서서는 index값을 key로 사용하였는데 이렇게 하면 배열이 변경될 때 효율적으로 리 렌더링 하지 못하게 된다.
지금까지 배운 개념을 응용하여 고정된 배열을 렌더링 하는 것이 아닌, 동적인 배열을 렌더링하는 것을 구현해보자.

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

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

export default IterationSample;

IterationSample 컴포넌트에서 useState를 사용하여 상태를 설정하였다.

  1. 데이터 배열
  2. 텍스트를 입력할 수 있는 input 상태
  3. 데이터 배열에서 새로운 항목을 추가할 때 사용할 고유 id를 위한 상태
  • 배열을 설정할 때에도, 단순 문자열로 이루어진 배열을 만든 것에 비해 이번에는 객체 형태로 이루어진 배열을 만들었다. 해당 객체에는 문자열과 고유 id값이 있다.
  • map 함수를 사용할 때에도 key값을 index값이 아닌 name.id값을 사용해주었다.

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); //새로운 항목을 추가할 때 사용할 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 nameList = names.map(name => <li key={name.id}>{name.text}</li>);
    return (
        <div>
            <input value={inputText} onChange={onChange} />
            <button onClick={onClick}>추가</button>
            <ul>{nameList}</ul>
        </div>
    );
};

export default IterationSample;

배열에 새 항목을 추가할 때 배열의 push 함수를 사용하지 않고 concat을 사용했다.

push 함수는 기존 배열 자체를 변경해주고

concat함수는 새로운 배열을 만들어 준다.

 

리액트에서 상태를 업데이트할 때는 기존 상태를 그대로 두면서 새로운 값을 상태로 설정해야 한다. (불변성 유지) 그래야 나중에 리액트 컴포넌트의 성능을 최적화할 수 있다.

3) 데이터 제거 기능 구현하기

불변성을 유지하면서 배열의 특정 항목을 지울 때는 배열의 내장 함수 filter를 사용한다.

filter함수는 배열에서 특정 조건을 만족하는 원소들만 쉽게 분류할 수 있다.

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

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

위의 filter 함수를 사용하여 IterationSample 컴포넌트의 항목 제거 기능을 구현해 보자.

HTML 요소를 더블클릭하여 제거할 건데 이때 사용되는 이벤트 이름은 onDoubleClick이다.

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); //새로운 항목을 추가할 때 사용할 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 nameList = names.map(name => <li key={name.id} onDoubleClick={() => onRemove(name.id)}>{name.text}</li>);
    return (
        <div>
            <input value={inputText} onChange={onChange} />
            <button onClick={onClick}>추가</button>
            <ul>{nameList}</ul>
        </div>
    );
};

export default IterationSample;

Quiz

  1. 반복되는 컴포넌트를 렌더링 하는 데 사용하는 함수의 이름은?
  2. 데이터 추가 기능을 구현할 때, 기존 배열 자체를 변경하지 않고 새로운 배열을 만들어 반환하는 함수의 이름은?
  3. 데이터 제거 기능을 구현할 때, 불변성을 유지하면서 배열의 특정 항목을 지울 때 사용되는 배열 내장 함수의 이름은?
더보기
더보기

1번 정답 map함수

2번 정답 concat함수

3번 정답 filter함수

728x90

관련글 더보기