티스토리 뷰

WEB/React

[React] 컴포넌트 반복

춘햄 2023. 3. 24. 16:40

웹 App을 만들다보면 <li> 태그와 같이 반복되는 코드를 작성할 때가 있다. 

 

이번 포스팅에서는 리액트 프로젝트에서 반복적인 내용을 효율적으로 보여주고 관리하는 방법을 다뤄보자.


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

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

 

arr.map(callback, [thisArg])

- callback: 새로운 배열의 요소를 생성하는 함수로 파라미터는 다음 세 가지이다.

 1. currentValue: 현재 처리하고 있는 요소

 2. index: 현재 처리하고 있는 요소의 index 값

 3. array: 현재 처리하고 있는 원본 배열

 

- thisArg(option): callback 함수 내부에서 사용할 this 레퍼런스

 

간단한 예로 아래와 같은 코드를 짤 수 있다.

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

let processed = numbers.map(function(num) {
	return num * num;
});

배열 내에 있는 원소의 제곱 값을 구하는 코드인데, ES6 문법으로 간단하게 작성할 수도 있다.

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

const result = numbers.map(num => num * num);

데이터 배열을 컴포넌트 배열로 변환

 같은 원리로 기존 배열로 컴포넌트로 구성된 배열을 생성할 수도 있다.

 

◎IterationSample.js

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

export default IterationSample;

 

배열에서 각 문자열을 <ul> 태그 내부로 뿌려주는 간단한 코드이다. 실행은 잘 되는 거 같은데... 콘솔창을 확인해보면 아래와 같은 경고가 떠있다.

"key" props 이 없다고 경고가 나오는데, key는 뭐지?


key

 리액트에서 key는 컴포넌트 배열을 렌더링했을 때 어떤 원소에 변동이 있었는지 알아내려고 사용한다. 예를 들어 유동적인 데이터를 다룰 때는 원소를 새로 생성할 수도, 제거할 수도, 수정할 수도 있다. key가 없을 때는 Virtual DOM을 비교하는 과정에서 리스트를 순차적으로 비교하면서 변화를 감지한다. 

 

문제는 없지만, key를 사용하여 변화를 확인하는 것이 훨씬 빠르기 때문에 경고를 띄우는 것이다.

 

key는 아래와 같이 설정할 수 있다.

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

    return <ul>{nameList}</ul>
};

export default IterationSample;

보통은 객체가 가진 고유 id 값을 키로 사용하지만, 그런 속성을 가지고 있지 않는 경우 위와 같이 index로 사용해도 무방하다. key props를 반복한 요소 내부에 넣어주기만 하면 된다.


이제 지금까지 배운 개념을 응용하여 고정된 배열을 렌더링하는 것이 아닌, 동적인 배열을 렌더링하는 예제를 구현해보려고 한다.

 

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

export default IterationSample;

useState를 사용하여 state 배열을 만들고, map 함수로 namesList를 받아서 그대로 리스트를 리턴했다. 

 

이제 기본적인 리스트에 추가 가능을 구현해보자. 그렇게 어려운 내용은 아니니, 따로 주석을 달지는 않았다.

 

◎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)

    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;

입력창에 입력된 데이터를 버튼을 눌렀을 때, names 배열에 다음 아이디와 함께 추가시킨 뒤에 아이디 값을 +1, 입력 텍스트를 공백으로 만드는 정도의 코드이다.

 

데이터 제거 기능도 마찬가지로 구현할 수 있다. 데이터 제거 기능은 arr의 filter 함수를 사용한다. 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)

    const onChange = e => setInputText(e.target.value);

    const onClick = () => {
        const nextNames = names.concat({
            id: nextId,
            text: inputText
        });

        setNextId(nextId + 1);
        setNames(nextNames);

        setInputText('');
    }

    const onDeleteClick = (id) => {
        const nextNames = names.filter(name => name.id !== id);
        setNames(nextNames);
    }

    const namesList = names.map(name => <li key={name.id} onDoubleClick={() => {onDeleteClick(name.id)}}>{name.text}</li>)

    return (
        <>
            <input value={inputText} onChange={onChange} />
            <button onClick={onClick}>추가</button>
            <ul>{namesList}</ul>
        </>
    )
};

export default IterationSample;


 

 

 

끝!

'WEB > React' 카테고리의 다른 글

[React] Hooks  (0) 2023.03.27
[React] 컴포넌트의 라이프사이클  (0) 2023.03.24
[React] ref: DOM에 이름 달기  (0) 2023.03.24
[React] 이벤트 핸들링  (0) 2023.03.23
[React] Component  (0) 2023.03.23
Comments