React - 스타일드 컴포넌트(styled-components)

김정욱·2020년 10월 3일
4

React

목록 보기
7/22
post-thumbnail
post-custom-banner

(본 글은 velopert님의 https://react.vlpt.us/styling/03-styled-components.html 를 참조합니다.)

styled-components

[ 설명 ]

: 순수 CSS로 스타일링 하는 문제점을 개선하기 위한 방법 중 하나
  JS 안에 CSS를 작성하는 여러 라이브러리 중 인기가 가장 많다
(자세한 설명 : https://blog.nerdfactory.ai/2019/10/25/react-styled-components.html)


[ 준비 ]

1) CRA 만들기
npx create-react-app styling-with-styled-components
2) styled-components 설치
yarn add styled-components
3) extenstion 설치
: styled-components 내부에서 css속성 자동완성을 하기 위한 확장툴


[ Template Literal 사용 & 조건 부 css 적용 ]

import React from 'react';
import styled, {css} from 'styled-components';

const Circle = styled.div`
  width: 5rem;
  height: 5rem;
  background: ${props => props.color || 'black'};
  border-radius: 50%;
  ${props =>
    props.huge &&
    css`
      width: 10rem;
      height: 10rem;
      `
  }
`;
function App() {
  return (
    <Circle color="red" huge/>
  );
}

[ & 사용 / lighten(), darken() 사용 ]

import React from 'react';
import styled from 'styled-components';
import { darken, lighten } from 'polished';

const StyledButton = styled.button` 

  /* 색상 */
  background: #228be6;
  &:hover {                
    background: ${lighten(0.1, '#228be6')};
  }
  &:active {
    background: ${darken(0.1, '#228be6')};
  }

  /* 기타 */
  & + & {
    margin-left: 1rem;
  }
`;

function Button({ children, ...rest }) {
  return <StyledButton {...rest}>{children}</StyledButton>;
}

export default Button;
  • &은 Sass 문법
    1) styled.button
        :<button>에 대한 스타일을 정의 하겠다.
    2) &.hover == .button.hover
    3) & + & == .button + .button
        : button끼리 붙어 있을 때

  • lightendarkenpolish 라이브러리로 사용
    설치 : $ yarn add polished
    4) lighten (0.1, '#228be6')
         : 지정한 색에서 10% 밝아 진다


[ ThemeProvider 사용하여 전역 변수 사용 ]

: 여러번 사용되는 객체전역 선언하여 쓰는 방법

1) App.js에서 선언

import styled, { ThemeProvider } from 'styled-components';

...

function App() {
  return (
    <ThemeProvider
      theme={{
        palette: {
          blue: '#228be6',
          gray: '#495057',
          pink: '#f06595'
        }
      }}

      <AppBlock>
        <Button>BUTTON</Button>
        <Button color="gray">BUTTON</Button>
        <Button color="pink">BUTTON</Button>
      </AppBlock>
    </ThemeProvider>
  );
}

: ThemeProviderstyled-component에서 꺼내와서 감싸준다.
   그리고 전역 변수로 지정할 값(ex) palette)을 선언한다.


2) Button.js에서 사용

const StyledButton = styled.button`
...
  /* 색상 */
  ${ ({thema, color}) => {
    const selected = theme.palette[color];
    return css`
      background: ${selected};
      &:hover {
        background: ${lighten(0.1, selected)};
      }
      &:active {
        background: ${darken(0.1, selected)};
      }
...
Button.defaultProps = {
    color: 'blue'
};

: prop.theme.palette전역 객체 접근
  prop.color로 전달받은 색상 접근
    따라서 !
 구조분해 문법으로 props에서 thema와 color를 빼서 정리
({thema, color})


[ Dialog 만들기 ]

( 기능 구현 )

1) Dialog.js 작성

import React from 'react'
import styled from 'styled-components';
import Button from './Button';

  // 배경을 고정 + 색 변경 + Dialogblock을 중앙에 배치
const DarkBackground = styled.div`
    position: fixed;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(0,0,0,0.8);
`;
 // DialogBlock 작성
const DialogBlock = styled.div`
    width: 320px;
    padding: 1.5rem;
    background: white;
    border-radius: 2px;
    h3{ /* DialogBlock 밑에 있는 h3요소에 적용 */
        margin: 0;
        font-size: 1.5rem;
    }
    p{  /* DialogBlock 밑에 있는 p요소에 적용 */
        font-size: 1.125rem;
    }
`;
const ButtonGroup = styled.div`
    margin-top: 3rem;
    display: flex;
    justify-content: flex-end;
`;

function Dialog({ title, children, confirmText, cancelText, onConfirm, onCancel, visible}) {
    if(!visible) return null;
    return (
    <DarkBackground>
        <DialogBlock>
          <h3>{title}</h3>
          <p>{children}</p>
          <ButtonGroup>
            <Button color="gray" onClick={onCancel}>{cancelText}</Button>
            <Button color="pink" onClick={onConfirm}>{confirmText}</Button>
          </ButtonGroup>
        </DialogBlock>
      </DarkBackground>
    )
}
Dialog.defaultProps = {
    confirmText: '확인',
    cancelText: '취소'
}
export default Dialog

2) App.js 작성

...
    <Dialog
    title="정말로 삭제하시겠습니까?"
    confirmText="삭제"
    cancelText="취소"
    onConfirm={onConfirm}
    onCancel={onCancel}
    visible={dialog}

      데이터를 정말로 삭제하시겠습니까?
    </Dialog>
...
  • Dialog.js에서 을 만든다.
  • 재사용을 위해 title / confirmText / cancelText를 받는다.
  • App.js에서 dialog라는 state를 통해 false/true로 관리한다.
  • 각 버튼 클릭시 내용은 App.js에서 콜백 함수로 전달
    (하위컴포넌트 -> 상위컴포넌트 는 콜백 함수를 통해 값 전달!)

( 트랜지션 구현 )

: Dialog가 나타나고 사라질때 생기는 트랜지션 효과
   styled-component에서 keyframe을 이용해서 구현한다


1) Dialog 등장할 때

   1-1) DarkBackground 쪽에는 서서히 나타나는 fadeIn 효과

import styled, { keyframes } from 'styled-components';

const fadeIn = keyframes`
  from {
    opacity: 0
  }
  to {
    opacity: 1
  }
`;

const DarkBackground = styled.div`
 ...
  animation-duration: 0.25s;
  animation-timing-function: ease-out;
  animation-name: ${fadeIn};
  animation-fill-mode: forwards;
`;
...

   1.2) DialogBlock는 아래에서부터 위로 올라오는 slideUp 효과

import styled, { keyframes } from 'styled-components';

const slideUp = keyframes`
  from {
    transform: translateY(200px);
  }
  to {
    transform: translateY(0px);
  }
`;

const DialogBlock = styled.div`
 ...
  animation-duration: 0.25s;
  animation-timing-function: ease-out;
  animation-name: ${slideUp};
  animation-fill-mode: forwards;
`;
...

[ 결과 확인 ]


2) Dialog 사라질 때

: 2개의 변수를 이용해 Dialog가 닫힐 때 setTimeout으로 잠시 지연시킨 뒤 애니메이션을 적용시켜야 한다.
   'animate' : 현재 트랜지션 효과를 보여주고 있는 상태를 의미하는 변수
   'localVisible' : 컴포넌트가 사라지는 시점을 지연시키기 위한 변수


  2.1) animate / localVisible 변수 선언

  const [animate,setAnimate] = useState(false);
  const [localVisible, setLocalVisible] = useState(visible);

  2.2) useEffect 작성

    useEffect(()=>{
        // visible 값이 true -> false 되는 시점에 수행!
        if(localVisible && !visible){
            setAnimate(true);
            setTimeout(()=>setAnimate(false),250);
        }
        setLocalVisible(visible);
    },[localVisible, visible])

  2.3) return null 조건 수정

    if(!animate && !localVisible) return null;

  2.4) DarkBackground에 fadeOut 설정

import styled, { keyframes, css} from 'styled-components';

  const fadeOut = keyframes`
  from {
    opacity: 1
  }
  to {
    opacity: 0
  }
`;

  const DarkBackground = styled.div`
   ...
    ${props =>
        props.disappear &&
        css`
          animation-name: ${fadeOut};
        `}
`;
...

  <DarkBackground disappear={!visible}>
    ...

  2.5) Dialog에 slideDown 설정

import styled, { keyframes, css} from 'styled-components';

const slideDown = keyframes`
  from {
    transform: translateY(0px);
  }
  to {
    transform: translateY(200px);
  }
`;

const DialogBlock = styled.div`
  ...
    ${props =>
        props.disappear &&
        css`
          animation-name: ${slideDown};
        `}
`;
...

  <DialogBlock disappear={!visible}>
    ...

[ 결과 확인 ]

profile
Developer & PhotoGrapher
post-custom-banner

0개의 댓글