Array Component

zzwwoonn·2022년 5월 22일
1

React

목록 보기
13/23

배열 컴포넌트의 활용 및 재활용

React 에서 배열 컴포넌트 사용시 key에 배열의 index는 사용하면 안된다.

자 그럼 배열 컴포넌트와 key 에 대해 알아보자.

배열 컴포넌트


// TodoList.jsx

import React from 'react';

function TodoList() {
    const todoList = [
        { taskName : '집에 바로 간다', finished : false },
        { taskName : '노래방 갔다가 집에 간다', finished : true }, 
    ];

    return(
        <div>
            <h1>TodoList</h1>
            <p> using array component and 'key' </p>
            {todoList.map( (todo) => (<div key={todo.taskName}> {todo.taskName} </div>) )}
        </div>
    );
}

export default TodoList;

// App.js
import './App.css';
import React from 'react';

import MyComponent from './03/MyComponent';
import ColorComponent from './03/ColorComponent';
import ChildComponent2 from './03/ChildComponent2'
import DefaultPropsComponennt from './03/DefaultPropsComponent'
import ChildProperty from './03/ChildProperty'

import Counter from './03/Counter'
import ListExample from './03/ListExample';
import TodoList from './03/TodoList';

function App() {
  
  return (
    <React.Fragment>
      <div>
        <TodoList/>
      </div>
    </React.Fragment>
  );
}

export default App;

코드를 보기 전에 App.js 에서 나오는 React.Fragment 를 먼저 보자.

React.Fragment

리액트 16.3 버전까지는 render() 함수는 트리 구조의 노드 1개만 반환할 수 있었다.
=> class component(클래스 컴포넌트)의 경우

function component (함수형 컴포넌트)의 경우도 (위와 마찬가지로) return 값으로 트리 구조의 노드 1개만 반환할 수 있다.

예를 들어 함수의 리턴값이 jsx 일 경우

return (
	<p> 이건 </p>
  	<span> 돌아가지 </span>
    <div> 않아 </div>
)

위와 같은 코드는 돌아가지 않는다.

따라서 여러 개의 노드를 반환하고 싶은 경우 의미 없는 최상위 노드를 추가해야 했다.
=> 어쩔 수 없이 div 엘리먼트를 추가해야 했다.

return (
	<div>
      <p> 이건 </p>
      <span></span>
      <div> 돌아가 </div>
  	</div>
)

<React.Fragment></React.Fragment> 는 <></> 로도 사용(표현?) 가능하다.


다시 원래 내용으로 돌아가서

TodoList와 같이 배열 컴포넌트를 재사용 해야 할 경우가 있다고 해보자.

예를 들어 배열 안에 3개의 항목이 있다고 했을 때 배열 렌더링을 진행하게 한다면 해당 리스트 변수에 1개가 더 추가되는 경우라도 React는 총 4개를 처음부터 다시 리렌더링 하게 된다.

key

하지만 key를 지정해준다면 기존의 요소들은 변경되지 않았다는걸 React에서 자동으로 파악 후 새로 생기는(추가되는) 요소에 대해서만 리렌더링을 진행하게 된다.

큰 작업 없이 key 요소만 추가했음에도 더욱 최적화 된 렌더링을 진행할 수 있는 것이다.

그렇다면 key 값으로 왜 index 를 넣으면 안되냐?

function TodoList() {
    const todoList = [
        { taskName : '집에 바로 간다', finished : false },
        { taskName : '노래방 갔다가 집에 간다', finished : true }, 
    ];

    return(
        <div>
            <h1>TodoList</h1>
            <p> using array component and 'key' </p>
            {todoList.map( (todo, i) => (<div key={`tl_${i}`}> {todo.taskName} </div>) )}
        </div>
    );
}
todoList.map((todo, i) => <div key = {`tl_${i}`})

=> map 함수에서 인자로 3개를 넣을 수 있다. 
-> 요소값, 인덱스, this(호출한 객체)

=> 오늘 모던 JS에서 공부한 내용, 모던 deep dive 540 page
=> 졸라 뿌듯

여기서 인덱스 값, 이거를 키 값으로 넣는다면!! 이라는 상황이다.

filter 함수나 map 함수를 추가하여 배열 항목에서 finished의 값이 false인 항목을 제외하는 등의 항목을 변형하는 경우 인덱스 번호를 키로 사용한다면 키 값이 함께 바뀌는 문제가 발생한다.

위의 경우 '노래방 갔다가 집에 간다' 의 키 값은 'tl_1' 에서 'tl_0'으로 바뀐다.

그러면 리액트가 배열 컴포넌트의 내용이 바꼈으니 이를 리렌더링 할텐데
(이 때 바뀌지도 않은 것도 전부 리렌더링 하는 게 아니라 바뀐거만 딱딱 해주려고 즉, 최적화된 리렌더링을 하려고 key 값을 쓰는건데)
키 값이 바뀌어 버렸으니 '노래방 갔다가 집에 간다' 항목의 컴포넌트를 재활용하지 못하고 새로 그리게 되어 효율이 좋지 않다.

요약하자면 결론은

결론

배열 항목에서 특정 값에 따라 항목을 제외하는 경우에 (filter 함수, map 함수 등) 인덱스 번호를 키로 사용한다면 키값이 함께 변경되는 문제가 발생
=> 단순한 리스트 렌더링 역할만 수행한다면 문제가 생기지 않지만 정렬, 추가, 삭제 등의 작업이 있는 경우 예상치 못한 문제가 발생

그렇기 때문에

React 에서 배열 컴포넌트 사용시 key에 배열의 index는 사용하면 안된다.

그럼 key 값으로 뭐를 써주면 되냐?

고유한 배열 항목을 사용하면 된다. 위의 예제에서는 taskName에 고유한 값이 들어있으므로 이를 키 값으로 사용하면 이전 키 값과 충돌하는 이 문제를 해결할 수 있다.
=> 제일 위에 있는 첫 코드가 이를 적용한 코드이다.

대부분의 경우는 각 배열 요소의 고유한 유니크 키 값이 존재할테지만 간혹 이렇지 못한 경우가 있을 수 있다. 이런 경우는 nanoid 와 같은 uniqueID 생성 라이브러리를 통해 key 속성을 넣어주면 된다.


// nano id 예제

import React, { useState } from 'react';
import { nanoid } from 'nanoid';

function PersonList() {
  const [personList, setPersonList] = useState([{ name: '사람1'}, { name: '사람2' }]);

  // 사람 추가 이벤트 
  const addPerson = () => {
    setPersonList((prev) => [{ name: '추가된사람', description: '추가된 사람 입니다.' }, ...prev]);
  };

  return (
    <div>
      <h2>배열 index key 예시</h2>
      <button onClick={addPerson}>사람 추가</button>
      {personList.map((person, index) => {
        return (
          // nanoid 를 사용한 고유 key 부여 
          <div key={nanoid()}>
            <span>{person.name}</span>
            <input type='text' />
          </div>
        );
      })}
    </div>
  );
}

export default PersonList;

nanoid

0개의 댓글