[styled-components] attrs()

박기영·2023년 1월 14일

styled-components

목록 보기
2/2

아주 먼 옛날, 처음 리액트를 배웠을 때 만들었던 간단한 정적 페이지를 다듬는 중이었다.
왜 다듬는지는 모르겠지만, 최근 코딩과 내 현실에 회의감이 들어서 도피하는 느낌이다..
애니메이션이 적용되었기 때문에 최적화를 해주는 것을 목표로 하고 있었는데,
프로젝트는 styled-components를 사용했다. 하나하나 최적화를 하던 중
애니메이션으로 인해 너무 많은 클래스가 발생한다며 콘솔에 경고가 발생했다.

참고 이미지

프로젝트에서는 스크롤 이벤트를 통해 이미지들이 움직이는 애니메이션이 적용되어있었고,
리액트 개발자 도구를 통해 살펴보니 애니메이션으로 인해서 리렌더링이 무지하게 발생하고 있었다.

우선 경고, 에러 메세지는 매우 친절할 확률이 높으므로,
attrs()라는 것에 대해 알아보기 시작했다.

언제 attrs()를 사용하는가?

You can pass in attributes to styled components using attrs, but it is not always sensible to do so.
The rule of thumb is to use attrs when you want every instance of a styled component to have that prop, and pass props directly when every instance needs a different one
- styled-components 공식 docs -

styled-componentspass props로 전달받았던 값들이
매 순간 달라진다면 attrs를 사용하라는 것 같다.

필자는 스크롤 이벤트로 받아온 스크롤 값을 pass props로 넘겨주고 있었다.

const LogoWrapper = styled.div`
  position: absolute;
  top: ${(props) => `${props.scrollForTop}px`};
  display: flex;
  justify-content: center;
  align-items: center;
  width: 100vw;
  height: 100vh;
  animation: ${ShowLogo} 1000ms ease-in-out;
`;

const LogoImg = styled.img`
  display: block;
  min-width: 500px;
  max-width: 1000px;
  width: ${(props) => `${1000 - props.scrollTop}px`};
  height: auto;
`;

스크롤을 내리면 매 순간 다른 값들이 컴포넌트에 전달되는 것이다.

개발자 도구를 통해 어떤 상황인지 살펴보자.

참고 동영상

위 영상은 필자가 스크롤을 했을 때, 컴포넌트들의 모습이다.
스크롤을 할 때마다 클래스가 변경되는 것을 확인 할 수 있다.
props가 계속 변경되니까 styled-components도 계속 변경되는 것이다.

이럴 때가 바로 attrs()를 사용할 때이다!

attrs()를 사용해보자

const LogoWrapper = styled.div.attrs({
  style: {
    position: "absolute",
    display: "flex",
    justifyContent: "center",
    alignItems: "center",
    width: "100vw",
    height: "100vh",
  },
})`
  top: ${(props) => `${props.scrollForTop}px`};
  animation: ${ShowLogo} 1000ms ease-in-out;
`;

const LogoImg = styled.img.attrs({
  style: {
    display: "block",
    minWidth: "500px",
    maxWidth: "1000px",
    height: "auto",
  },
})`
  width: ${(props) => `${1000 - props.scrollTop}px`};
`;

attrs() 내부에 작성해준 객체가 컴포넌트의 속성으로 들어간다.
이게 무슨 말인가?

참고 이미지

attrs() 내부에 작성한 속성들이 컴포넌트의 인라인 속성으로 들어가 있는 것을 확인 할 수 있다.
이를 활용하여 공통적인 부분을 계속 재생성할 필요없이 최적화가 가능하다.

하지만, 개발자 도구를 살펴보면
여전히 스크롤에 의한 클래스 재생성이 발생한다.
당연하게도...여젼히 pass props로 스크롤 값을 전달하고 있기 때문이다.

지속적으로 변화되는 속성값 최적화

저 에러를 해결하려면, 지속적으로 클래스가 업데이트 되는 것을 막아야한다.
방금 위에서 살펴봤듯, attrs()를 사용하면 특정 값들을 인라인 속성으로 넣어줄 수 있었다.
이걸 활용하여, 지속적으로 변경되는 입력값에 대해 클래스가 아닌 인라인 스타일이 변경되도록 해보자.

const LogoWrapper = styled.div.attrs((props) => ({
  style: {
    position: "absolute",
    display: "flex",
    justifyContent: "center",
    alignItems: "center",
    width: "100vw",
    height: "100vh",
    top: `${props.scrollForTop}px`,
  },
}))`
  animation: ${ShowLogo} 1000ms ease-in-out;
`;

const LogoImg = styled.img.attrs((props) => ({
  style: {
    display: "block",
    minWidth: "500px",
    maxWidth: "1000px",
    height: "auto",
    width: `${1000 - props.scrollTop}px`,
  },
}))``;

짠! 이렇게 하면 모든 스타일 속성이 attrs()에 들어갔기 때문에,
클래스가 변경되는게 아니라, 인라인 스타일 값이 변경될 것이다.

개발자 도구를 통해 의도한 대로 작동하는지 살펴보자.

참고 동영상

성공! 스크롤 값에 따라 인라인 스타일이 변경된다! 클래스는 그대로 남아 있는 것을 확인할 수 있다.

동적으로 속성 부여하기

또한, 이를 활용하여 동적으로 속성을 부여하는 것이 가능하다.
stackoverflow에 올라와 있는 예시를 하나 살펴보자.

const Input = styled.input.attrs(({ type }) => ({
  type:  type || "password"
}))`
  align-items: center;
  display: flex;
  margin: 1.5vh 0;
`

이런 식으로 input 태그의 type 속성을 동적으로 설정할 수 있게되는 것이다.
아래와 같은 효과를 얻을 수 있다.

<Input .../> // renders type="password"
<Input type="text" .../>
<Input type="email" .../>

attrs() 내에 작성된 값들을 컴포넌트(혹은 엘리먼트)의 속성으로 바로 넣어줄 수 있다는 점이
styled-components의 재사용성을 높여줄 것 같다!

참고 자료

ayaan92님 블로그
stackoverflow 질문글
styled-components 공식 docs - atrrs

profile
나를 믿는 사람들을, 실망시키지 않도록

0개의 댓글