Styled 컴포넌트를 사용하면서 아래와 같은 warning이 계속해서 뜨는것을 확인했다.
Console warning 1.
React does not recognize the
keywordType
prop on a DOM element.
If you intentionally want it to appear in the DOM as a custom attribute, spell it as lowercasekeywordtype
instead.
If you accidentally passed it from a parent component, remove it from the DOM element.
Console warning 2.
Warning: Received
true
for a non-boolean attribute속성
.
If you want to write it to the DOM, pass a string instead: 속성="true" or 속성={value.toString()}.
콘솔창에 수없이 많이뜨는 위 두 경고 문구를 보고 왜 뜨는 건지 원인 과 해결방법을 찾아보았다.
React does not recognize the
keywordType
prop on a DOM element.
적힌 그대로 React DOM이 알지 못하는 키워드여서 나오는 warning 이다.
Warning: Received
true
for a non-boolean attribute attribute.
HTML attribute로 boolean 값이 들어갔을떄 나는 warning 이다.
위 에러들은 HTML의 표준 속성에 대해 알고 넘어가야 한다.
기본적으로 HTML 시멘틱 태그는 width, height 등등 다양한 속성을 가지고 있다.
이러한 속성을 HTML 표준 속성
이라고 부른다.
이 표준속성 리스트 외 styled를 위해, 다른 custom 속성을 전달하게 된다면, 위와 같은 에러들이 난다.
Styled 컴포넌트는 기본적으로 모든 custom 속성을 HTML attribute로 전달 한다.
const Link = props => (
<a {...props} className={props.className}>
{props.text}
</a>
)
const StyledComp = styled(Link)`
color: ${props => (props.red ? 'red' : 'blue')};
`
<StyledComp text="Click" href="https://www.styled-components.com/" red />
만약 다음과 같이 red라는 props를 Styled로 넘기게 된다면
<a text="Click" href="https://www.styled-components.com/" red="true" class="[generated class]">Click</a>
HTML DOM 에는 위외 같이 red가 있는 상태로 렌더링 될 것이다.
Styled는 custom props들이 React props 로 전달 되거나, DOM 요소로 렌더링 되지 않도록 기호($)를 prefix로 붙이는 기능을 제공한다.
render(
<Comp $draggable="red" draggable="true">
Drag me!
</Comp>
);
const Comp = styled.div`
color: ${props =>
props.$draggable || 'black'};
`;
위와 같이 작성하면 $draggable
는 Styled로 전달되지만 HTML attribute로는 추가되지 않는다.
만약 $를 붙이는 방식이 마음에 들지 않는다면 사용 할 수 있는 방법이다.
두번째 방법으로는 Styled의 shouldForwardProp 기능을 사용하는 것이다.
const Comp = styled('div').withConfig({
shouldForwardProp: (prop) =>
!['hidden'].includes(prop),
}).attrs({ className: 'foo' })`
color: red;
&.foo {
text-decoration: underline;
}
`;
위 와 같이 styled의 Config 를 통해 shouldForwardProp를 콜백 함수를 받을 수 있다.
콜백함수의 첫번쨰 인자로 prop에는 propName 들이 들어오게 되고, true를 리턴한다면, HTML attribute또는 React Props 로 넘길 수 있고, false 를 리턴한다면 Styled 에서만 사용가능한 custom Props 로 만들 수 있다.
위의 transient props($)의 기능도 내부적으로 보면
shouldForwardProp이 global Config로서 적용되어 있는 로직이다.
shouldForwardProp: (propName) => {
return !propName.startWith('$')
}
위의 문제를 해결한다면 다음과 같은 이점이 있다.
Styled-components 를 사용할 때 불필요한 속성이 DOM 에 전달되는 것을 방지 할 수 있다.
$ prefix 를 사용함으로서 React props 로 넘어가는 속성과 Styled props 로 넘어가는 속성을 명시적으로 구분할 수 있다.
위의 $를 붙이는 transient props 방식이나 shouldForwardProsps 방식을 이용해 warning 을 해결하는 방식 모두 문제를 해결하는 방법이지만,
Styled props 변수명을 $로 모두 수정하거나, 각각의 Styled 컴포넌트 Config에 shouldForwardProps 를 넘기고 싶지 않을 경우 아래와 같은 wrapper 함수를 만드는 방식으로 문제를 해결 할 수 있다.
// styled.jsx file
import styled from 'styled-components';
const styledWrapper = (tag) => {
return styled(tag).withConfig({
shouldForwardProp: (prop) =>
isPropValid(prop)
}
export const styledWrapper
@emotion/is-prop-valid 를 사용하면 HTML의 표준속성을 자동적으로 걸려줄 수 있다.
import isPropValid from '@emotion/is-prop-valid'
isPropValid('href') // true
isPropValid('someRandomProp') // false
하지만 위와 같이 사용하게 되면 기존 태그
들은 문제 없이 동작하지만, 아래와 같이 Styled를 통해서 컴포넌트
를 override 하는 경우는 Child Component로 넘겨야할 props 조차도 전달이 막히는 문제가 발생한다.
const Comp = styledWrapper(OtherComp)``
이는 조건문을 걸어 해결 할 수 있지만 override 된 Styled는 따로 shouldProps를 적용해 주어야 한다.
// styled.ts file
import _styled from 'styled-components';
const styled = ((component: any, config: any) => {
if(typeof tag === 'string') {
config = {
shouldForwardProp: (prop: string) =>
isPropValid(prop),
...config
};
}
return _styled(component, config);
}) as (typeof _styled);
const tags = 'a|abbr|address|area|article|aside|audio|b|base|bdi|bdo|big|blockquote|body|br|button|canvas|caption|cite|code|col|colgroup|data|datalist|dd|del|details|dfn|dialog|div|dl|dt|em|embed|fieldset|figcaption|figure|footer|form|h1|h2|h3|h4|h5|h6|head|header|hgroup|hr|html|i|iframe|img|input|ins|kbd|keygen|label|legend|li|link|main|map|mark|marquee|menu|menuitem|meta|meter|nav|noscript|object|ol|optgroup|option|output|p|param|picture|pre|progress|q|rp|rt|ruby|s|samp|script|section|select|small|source|span|strong|style|sub|summary|sup|table|tbody|td|textarea|tfoot|th|thead|time|title|tr|track|u|ul|var|video|wbr|circle|clipPath|defs|ellipse|foreignObject|g|image|line|linearGradient|mask|path|pattern|polygon|polyline|radialGradient|rect|stop|svg|text|tspan'.split('|');
for(const tag of tags){
(styled as any)[tag] = styled(tag as any);
}
export default styled;
감사합니다. 이런 정보를 나눠주셔서 좋아요.