Peony의 기록 창고 🌼
article thumbnail
Published 2022. 3. 21. 21:48
[React] 컴포넌트 반복 React
반응형

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

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

 

문법

arr.map(callback, [thisArg])
  • callback : 새로운 배열의 요소를 생성하는 함수
    • currentValue : 현재 처리하고 있는 요소
    • index : 현재 처리하고 있는 요소의 index값
    • array : 현재 처리하고 있는 원본 배열
  • thisArg(선택항목) : callback 함수 내부에서 사용할 this 레퍼런스

 

예제

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

export default IterationSample;
//App.js
import IterationSample from './IterationSample';


function App() {

  return <IterationSample />;

};

export default App;

image

실행 화면을 보면 원하는 대로 렌더링이 되었다. 하지만, console 창에서 "key"prop이 없다는 경고 메세지를 표시한 것을 볼 수 있다. 여기서의 key란 무엇일까?

 

Key

  • key 값 : map() 함수를 호출할 때 인자로 넘기는 Callback 함수의 인자로 넘어오는 index 값
  • 컴포넌트를 렌더링 하였을 때 어떤 원소가 변경되었는지 빠르게 감지하기 위해 사용
  • 만약 key가 설정되지 않았다면?
    가상 DOM을 순차적으로 비교하면서 감지하기 때문에 key가 없을때보다 속도가 느리다.

key 설정

  • map 함수의 인자로 전달되는 함수 내부에서 컴포넌트 props를 설정하듯이 설정
  • key 값은 언제나 유일해야 함 -> 데이터가 가진 고유 값을 key값으로 설정해야 한다.
  • 만약 고유한 값이 없으면? => index 값을 key로 사용
  • index를 key로 사용하면 배열이 변경될 때 효율적으로 리렌더링하지 못함.
//IterationSample.js
const IterationSample = () => {
    const names = ['눈사람', '얼음', '눈', '바람'];
    const nameList = names.map((name, index) => <li key = {index} >{ name } </li>);
    return <ul>{ nameList }</ul>
};

export default IterationSample;

 

응용 - 동적인 배열을 렌더링하기

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 nameList = names.map(name => <li key = {name.id} >{ name.text } </li>);
    return <ul>{ nameList }</ul>
};

export default IterationSample;

 

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

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

//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 nameList = names.map(name => <li key = {name.id} >{ name.text } </li>);
    return (
        <>
        <input value = { inputText } onChange = { onChange } />
        <button>추가</button>
        <ul>{ nameList }</ul>
        </>
    );
};

export default IterationSample;

 

2-1. onClick 이벤트 설정

  • 배열의 내장함수 concat을 사용하여 새로운 항목을 추가한 배열을 만들고, setNames를 통해 상태 업데이트
  • 새로운 항목을 추가할 때, 객체의 id값 : nextId값을 사용
  • 클릭될 때 마다 값이 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 onChange = e => setInputText(e.target.value);
    const onClick = () => {
        const nextNames = names.concat({
            id : nextId, //nextId 값을 id로 설정하고
            text : inputText
        });
        setNextId(nextId + 1);
        setNames(nextNames);
        setInputText(''); // inputText를 비운다.
    }

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

export default IterationSample;

 

push vs concat

push : 기존 배열 자체를 변경

concat : 새로운 배열을 만들어줌

 

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

  • 각 항목을 더블클릭했을 때 해당 항목이 화면에서 사라지는 기능 구현
    • 불변성을 유지하면서 업데이트 -> 배열의 내장함수 filter 사용
//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);
        setNames(nextNames);
        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 (
        <>
        <input value = { inputText } onChange = { onChange } />
        <button onClick={ onClick }>추가</button>
        <ul>{ nameList }</ul>
        </>
    );
};

export default IterationSample;

 

정리

  1. 컴포넌트 배열을 렌더링할 때, key 값 설정에 항상 주의해야한다.(유일성)
  2. 상태 안에서 배열을 변형할 때, 배열에 직접 접근하여 수정하는 것이 아니라, concat, filter 등의 배열 내장 함수를 사용하여 새로운 배열을 만든 후 이를 새로운 상태로 설정해주어야한다.

 

이 글은 김민준 님의 리액트를 다루는 기술을 읽고 정리한 내용입니다.

 

반응형
profile

Peony의 기록 창고 🌼

@myeongju