[React] 객체를 prop으로 하위 컴포넌트로 전달할 때 컴포넌트 메모

Seungrok Yoon (Lethe)·2024년 8월 13일
0

[TIL] 성장 한 스푼

목록 보기
51/53

이전이야기

[React] 중첩된 커스텀 훅의 리렌더링

memo 를 활용하여 불필요한 렌더링 방지

import styled from '@emotion/styled';
import useNestedHook from '@Hook/nested-custom-hook/useNestedHook';
import { memo } from 'react';
import { v4 as uuidv4 } from 'uuid';

export default function NestedCustomHook() {
  const formId = `user_${uuidv4()}`;
  const { formValues, setter } = useNestedHook();

  return (
    <VerticalFlexContainer>
      <form>
        <label htmlFor={`name_${formId}`}>
          Name:&nbsp;
          <input
            id={`name_${formId}`}
            name="name"
            type="text"
            value={formValues.name}
            onChange={(e) => {
              setter.setName(e.target.value);
            }}
          />
        </label>
        <label htmlFor={`email_${formId}`}>
          Email:&nbsp;
          <input
            id={`email_${formId}`}
            name="email"
            type="text"
            value={formValues.email}
            onChange={(e) => {
              setter.setEmail(e.target.value);
            }}
          />
        </label>
      </form>
      <HorizontalFlexContainer gap="20px">
        <MemoizedSpan label="Name" data={formValues.name} />
        <MemoizedSpan label="email" data={formValues.email} />
      </HorizontalFlexContainer>
    </VerticalFlexContainer>
  );
}

const MemoizedSpan = memo(
  ({ label, data }: { label: string; data: string }) => {
    console.log('MemoizedSpan', label, data);
    return (
      <CustomSpan>
        {label}: {data}
      </CustomSpan>
    );
  }
);

const HorizontalFlexContainer = styled.div<{ gap: string }>`
  display: flex;
  gap: ${(props) => props.gap};
`;

const VerticalFlexContainer = styled.div`
  display: flex;
  flex-direction: column;
  gap: 1rem;
  padding: 12px;
`;

const CustomSpan = styled.span`
  display: inline-block;
  background-color: 'red';
`;

MemoizedSpan이라는 컴포넌트를 임의로 선언해 보았다(emotion/styled는 무시하자). 공식문서대로라면, prop이 바뀌지 않는 부분의 컴포넌트는 리렌더링이 되지 않아야 한다.

실제로 잘 동작한다. Name 인풋을 수정할 때 email 쪽의 Span컴포넌트는 리렌더링 되지 않는다.

물론 memo를 사용하더라도 memo된 컴포넌트의 내부 state가 변경되거나 사용 중인 context가 변경되면 리렌더링 된다는 점에는 주의해야 한다.

객체를 props로 넘기는 경우(작성중)

객체를 props로 넘기는 경우는 컴포넌트를 작성하면서 자주 접하는 상황이다.

memo는 내부적으로 Object.is 로 비교를 하여 prop이 이전 렌더링 때의 prop과 동일한 지를 비교한다.

그러면 Object.is는 어떻게 동작을 할까?

Object.is

내가 궁금한 점은 중첩객체일 때 과연 Object.is가 제대로 동작하느냐였다. 실무에서 prop에 중첩객체를 전달해야 하는 경우가 잦았는데 이 경우에도 제대로 객체 간 비교를 수행하는지 궁금했다.

console.log(Object.is({ name: 'child1' }, { name: 'child1' }));
//false

props변경을 최소화하기

나는 상태를 변경했는데, 리렌더링이 되지 않는 경우

prop이 변경되지 않은 컴포넌트가 함께 리렌더링이 되는 경우

profile
안녕하세요 개발자 윤승록입니다. 내 성장을 가시적으로 기록하기 위해 블로그를 운영중입니다.

0개의 댓글