styled-components에서 boolean형 prop을 넘길 때 주의해야 할 점

aken·2023년 12월 7일
0

styled-components로 생성한 컴포넌트에서 boolean형 prop을 넘길 때, 화면에는 정상적으로 작동됐지만 개발자 도구에서 여러 오류가 마주쳤다. 문제의 코드부터 살펴보자.

interface Props {
  isActive: boolean;
}

const Label = ({ isActive }: Props) => {
  return (
    <LabelLayout isActive={isActive}>
      Label
    </LabelLayout>
  );
};

const LabelLayout = styled.button<{ isActive: boolean }>`
  ${({ isActive }) => (isActive ? activeLabelStyle : defaultLabelStyle)}
  // ...
`;

styled로 만든 컴포넌트의 boolean형 prop 네이밍

첫 번째로 마주친 에러 메시지는 다음과 같다.

Warning: React does not recognize the isActive prop on a DOM element. If you intentionally want it to appear in the DOM as a custom attribute, spell it as lowercase isactive instead. If you accidentally passed it from a parent component, remove it from the DOM element.

번역하면 React는isActive prop을 인식하지 못하기 때문에
커스텀 속성을 DOM에 의도적으로 나타내고 싶다면 소문자로 입력하고,
만약 부모 컴포넌트로부터 받은 prop이라면 제거해야 한다.

1. 소문자

에러 메세지에서 제안한 방법으로 먼저 소문자로 네이밍을 지어봤더니 에러가 해결됐다.

const Label = ({ isActive }: Props) => {
  return (
    <LabelLayout isactive={isActive}>
      Label
    </LabelLayout>
  );
};

const LabelLayout = styled.button<{ isactive: boolean }>`
  // ...
  ${({ isactive }) => (isactive ? activeLabelStyle : defaultLabelStyle)}
`;

그러나 isActive는 스타일만을 위한 속성이기 때문에 실제로 DOM에 속성으로 반영할 필요는 없었다. 정말 카멜 케이스로 작성할 수 있는 방법이 없을까?

2. prefix $ (5.1 버전부터 반영)

isActive 앞에 $를 붙여 $isActive로 작성하면 되는데, 이를 transient props라고 한다. transient은 "잠시 동안만 존재하는" 뜻으로, transient props는 React node나 DOM 요소에 prop이 반영되지 않도록 한다. 즉, 실제 요소에 prop이 attribute로 표시되지 않는다.

const Label = ({ isActive }: Props) => {
  return (
    <LabelLayout $isActive={isActive}>
      Label
    </LabelLayout>
  );
};

const LabelLayout = styled.button<{ $isActive: boolean }>`
  // ...
  ${({ $isActive }) => ($isActive ? activeLabelStyle : defaultLabelStyle)}
`;

Received false for a non-boolean attribute

만약 prop 네이밍을 소문자로 작성했다면(isactive) 아래와 같은 에러 메세지를 마주쳤을 것이다.

Warning: Received false for a non-boolean attribute isactive.
If you want to write it to the DOM, pass a string instead: isactive="false" or isactive={value.toString()}.
If you used to conditionally omit it with isactive={condition && value}, pass isactive={condition ? value : undefined} instead.

boolean형이 아닌 isactive의 값이 false라고 에러 메시지가 떴다. 이를 해결하기 위해, 두 가지 방법을 제시했다.

  1. 만약 isactive를 DOM의 속성으로 반영하고 싶다면 boolean 값을 string으로 변환한다.
  2. 만약 isactive를 DOM에 반영하고 싶지 않다면 삼항연산자를 사용하여 값으로 넘긴다.

boolean 값을 string으로 변환

const Label = ({ isActive, px = 20, py = 8 }: Props) => {
  return (
    <LabelLayout isactive={isActive.toString()}>
      Label
    </LabelLayout>
  );
};

const LabelLayout = styled.button<{ isactive: string }>`
  // ...
  ${({ isactive }) => (isactive ? activeLabelStyle : defaultLabelStyle)}
`;

boolean 값을 string으로 변환하여 넘겨줬더니 실제 DOM에 isactive가 반영된 것을 확인할 수 있었다.

<button isactive="false" class="sc-gsTDqH juywjJ">Label</button>

삼항 연산자

isActive가 true라면 1을, false라면 undefined를 넘길 것이다.

const Label = ({ isActive, px = 20, py = 8 }: Props) => {
  return (
    <LabelLayout isactive={isActive ? 1 : undefined}>
      Label
    </LabelLayout>
  );
};

const LabelLayout = styled.button<{ isactive: number | undefined }>`
  // ...
  ${({ isactive }) => (isactive ? activeLabelStyle : defaultLabelStyle)}
`;

삼항 연산자를 적용했더니 실제 DOM에 isactive가 반영되지 않은 것을 확인할 수 있었다.

<button class="sc-bdfCDU bRNsf">Label</button>

정리

  1. boolean형 prop을 실제 DOM에 반영하고 싶다면 prop 네이밍을 소문자로 작성하고, prop 값을 boolean에서 string으로 변환한다.

  2. boolean형 prop을 실제 DOM에 반영하고 싶지 않다면,
    a. prop 앞에 $를 붙인다. ($isActive)
    b. prop 네이밍을 소문자로 작성하고, 삼항 연산자을 통해 값을 전달한다.

마치며

필자는 $isActive로 하기로 정했다. isActive를 실제 DOM에 반영되지 않길 원했고, $를 붙이는게 더 깔끔해 보였다.

DOM에 의도적으로 나타내기 위해 소문자를 권장했지만, 이를 DOM에 반영하지 않기 위해 삼항 연산자를 사용하는 것이 모순적으로 느껴졌다.

참고

styled-components 공식 문서: transient-props
stackoverflow
Warning Received true for non-boolean attribute :: 마이구미

0개의 댓글