Key와 리렌더링의 관계(map, filter)

</>·2021년 11월 15일
4

Get to Know React

목록 보기
6/8
post-thumbnail
post-custom-banner

목표

  • Array 메서드인 Map, Filter에 대해 알아본다.
  • Key와 리렌더링의 관계를 알아본다.

1.Map, Filter

1-1. Map

  • 다음 코드는 해야할 할 일을 나열하는 코드이다.
const todo = [
    { id: 1, value: "go to cafe" },
    { id: 2, value: "studying" },
    { id: 3, value: "go to restaurant" },
    { id: 4, value: "running" }
  ];

  const App = () => {
    const [items, setItems] = React.useState(todo);

    return (
      <>
        {items.map((item) => (
          <div className="wrapper">
            <span>{item.value}</span>
            <button className="done-button">Done</button>
          </div>
        ))}
      </>
    );
  };
 
  • 자바스크립트 배열의 map 메서드를 사용해서 각각의 요소를 순서대로 불러 item들을 나열한다.

Array.prototype.map()

  • map() 메서드는 배열 내의 모든 요소 각각에 대하여 주어진 함수를 호출한 결과를 모아 새로운 배열을 반환하는 역할을 한다.

  • arr.map(callback(currentValue[, index[, array]])[, thisArg])

  • 콜백 함수의 매개 변수로 currentValue(처리할 현재 요소), index(처리할 현재 요소의 인덱스), array(map을 호출한 배열), thisArg(callback을 실행할 때 this로 사용되는 값)가 올 수 있다.
  • 그 중에서도 currnetValue는 필수 인자이다. 나머지 index, array, thisArg는 상황에 따라 넣을 수 있는 선택 인자이다.

  • 자세한 내용은 Mozila - Array.prototype.map()를 참조하면 된다.

🤔 의문

  • 할 일을 할 때마다 Done 버튼을 눌러 할 일을 지우려면 어떻게 해야 할까?

여러 방법이 있지만 Array의 filter 메서드를 사용하면 쉽게 지울 수 있다.


1-2. Filter

    // 생략

    const onClick = (todo) => {
      setItems((items) => items.filter((item) => item !== todo));
    };

    return (
      <>
        {items.map((item) => (
          <div className="wrapper">
            <span>{item.value}</span>
            <button className="done-button" onClick={() => onClick(item)}>
              Done
            </button>
          </div>
        ))}
      </>
    );

filter

  • filter 메서드를 사용하여 매개변수로 넘겨준 todo와 일치하지 않는 것을 찾아서 todo를 제외한 나머지 배열을 리턴한다.

Array.prototype.filter()

  • filter() 메서드는 주어진 함수의 테스트를 통과하는 모든 요소를 모아 새로운 배열로 반환한다.

  • arr.filter(callback(element[, index[, array]])[, thisArg])

  • 위에서 언급했던 map함수와 매개변수로 들어갈 수 있는 인자는 같다.
  • 콜백 함수의 매개 변수로 element(처리할 현재 요소), index(처리할 현재 요소의 인덱스), array(map을 호출한 배열), thisArg(callback을 실행할 때 this로 사용되는 값)가 올 수 있다.
  • 그 중에서도 element 필수적인 인자이며, 나머지 index, array, thisArg는 상황에 따라 넣을 수 있는 선택적인 인자이다.

  • 자세한 내용은 Mozila - Array.prototype.filter()를 참조하면 된다.

위에서 작성한 코드가 문제 없이 돌아가는 건 맞지만 콘솔에는 에러가 계속 찍힌다.

key

  • 각 엘리먼트들은 unique한 key 값을 가져야 한다는 내용의 에러이다.

2. Key

2-1. key란?

  • Key는 Value를 특정하는 이름을 뜻한다.
  • JSON, Object, Map, Dictionary 등에 쓰인다.

  • todo 순서를 바꿔서 늘리고 컴포넌트에 key를 주지 않았을 때와 key를 주었을 때를 비교해 보려고 한다.
const todo = [
        [
          { id: 1, value: "go to cafe" },
          { id: 2, value: "studying" },
          { id: 3, value: "go to restaurant" },
          { id: 4, value: "running" }
        ],
        [
          { id: 2, value: "studying" },
          { id: 3, value: "go to restaurant" },
          { id: 4, value: "running" },
          { id: 1, value: "go to cafe" }
        ],
        [
          { id: 3, value: "go to restaurant" },
          { id: 4, value: "running" },
          { id: 1, value: "go to cafe" },
          { id: 2, value: "studying" }
        ],
        [
          { id: 4, value: "running" },
          { id: 1, value: "go to cafe" },
          { id: 2, value: "studying" },
          { id: 3, value: "go to restaurant" }
        ]
      ];

2-2. 컴포넌트에 key를 주지 않으면?

    const [items, setItems] = React.useState(todo[0]);

    React.useEffect(() => {
      const interval = setInterval(() => {
        const random = Math.floor(Math.random() * 4);
        setItems(todo[random]);
      }, 1000);

      return () => {
        clearInterval(interval);
      };
    }, []);
  • 1초마다 random하게 접근하면 다음과 같은 현상이 벌어진다.

no_key

  • 탭은 두 번째로 고정되어 있는데 내용은 계속 바뀌는 문제가 발생한다. 이는 컴포넌트 자체가 바뀌지 않았기 때문에 일어난다.
  • 이는 컴포넌트를 완전히 재사용할 수 있다고 말할 수 없다.

🤔 의문

  • 그렇다면 어떻게 해야 완전히 컴포넌트를 재사용할 수 있을까?

2-3. 컴포넌트에 key 적용

    return (
      <>
        {items.map((item) => (
          <div className="wrapper" key={item.id}>
            <button className="todo" onClick={() => onClick(item)}>
              {item.value}
            </button>
          </div>
        ))}
      </>
    );
  • 컴포넌트 key 프로퍼티에 고유한 id를 부여하여 적용하면 다음과 같이 컴포넌트 단위로 이동한다.

key

  • 이로써 컴포넌트의 재사용이 가능해졌다.

🤔 의문

  • 그렇다면 위에서 배웠던 map() 함수의 index 파라미터를 사용해도 똑같이 동작할까?
    return (
      <>
        {items.map((item, index) => (
          <div className="wrapper" key={index}>
            <button className="todo" onClick={() => onClick(item)}>
              {item.value}
            </button>
          </div>
        ))}
      </>
    );
  • 결론부터 말하면 key 프로터피를 주지 않았을 때와 똑같은 현상이 발생한다. 즉, 컴포넌트를 재사용할 수 없다.
  • 이는 예전에 배웠던 재조정(reconciliation)과 관련이 있는데 React에서는 다음과 같이 말한다.

react-key1

react-key2

  • 이러한 이유로 위의 코드에서는 항목들이 1초마다 재배열 되었기 때문에 비효율적으로 동작한 것이다.

출처

profile
개발자가 되고 싶은 개발자
post-custom-banner

0개의 댓글