Emotion

오늘은 Emotion에 대해 소개해드리겠습니다 !

Emotion의 장점 🌟

  • css-in-js 형식으로 스타일을 사용할 수 있다.
  • className이 자동으로 부여되기 때문에 스타일이 겹칠 염려가 없다.
  • 재사용이 가능하다.
  • props, 조건 등에 따라 스타일을 지정 가능하다.
  • vendor-prefixing, nested selectors, and media queries 등이 적용되어 작성이 간편하다. ( 알기 쉽게 반응형 이라고 생각하시면 됩니다 :) )
  • styled component 사용방식과 css prop 기능을 지원하여 확장에 용이하다. ( 앱에서는 styled component를 많이 사용하지만 웹에서는 prop 기능때문에 Emotion을 많이 사용하는 것 같습니다. 개발자 차이이지만 css, scss를 사용하거나 styled component, emotion등을 사용 하는 것은 개발자 차이입니다! )
  • styled component보다 파일 사이즈가 작고, ssr시 서버 작업이 필요없다. ( ssr 이란 서버사이드 랜더링입니다!) 서버사이드랜더링 플로우 입니다

사용법 📘

css prop

import { css } from '@emotion/react'

const color = 'white'

render(
  <div
    css={css`
      padding: 32px;
      background-color: hotpink;
      font-size: 24px;
      border-radius: 4px;
      &:hover {
        color: ${color};
      }
    `}
  >
    Hover to change color.
  </div>
)

composition

import { css } from '@emotion/react'

const danger = css`
  color: red;
`const base = css`
  background-color: darkgreen;
  color: turquoise;
`render(
  <div>
    <div css={base}>This will be turquoise</div>
    <div css={[danger, base]}>
      This will be also be turquoise since the base styles
      overwrite the danger styles.
    </div>
    <div css={[base, danger]}>This will be red</div>
  </div>
)

styled component

import styled from '@emotion/styled'

const Button = styled.button`
  padding: 32px;
  background-color: hotpink;
  font-size: 24px;
  border-radius: 4px;
  color: black;
  font-weight: bold;
  margin-bottom: ${props => props.value ? '4px' : '2px'}
  &:hover {
    color: white;
  }
`render(<Button>This my button component.</Button>)

Object styles

import { jsx } from '@emotion/react'

render(
  <div
    css={{
      color: 'darkorchid',
      backgroundColor: 'lightgray'
    }}
  >
    This is darkorchid.
  </div>
)

import styled from '@emotion/styled'

const Button = styled.button(
  {
    color: 'darkorchid'
  },
  props => ({
    fontSize: props.fontSize
  })
)

render(
  <Button fontSize={16}>
    This is a darkorchid button.
  </Button>
)

Child Selectors

import { jsx } from '@emotion/react'

render(
  <div
    css={{
      color: 'darkorchid',
      '& .name': {
        color: 'orange'
      }
    }}
  >
    This is darkorchid.
    <div className="name">This is orange</div>
  </div>
)

media Queries

import { jsx } from '@emotion/react'

render(
  <div
    css={{
      color: 'darkorchid',
      '@media(min-width: 420px)': {
        color: 'orange'
      }
    }}
  >
    This is orange on a big screen and darkorchid on a small
    screen.
  </div>
)

Reusable Media Queries

import {  css } from '@emotion/react'

const breakpoints = [576, 768, 992, 1200]

const mq = breakpoints.map(
  bp => `@media (min-width: ${bp}px)`)

render(
  <div>
    <div
      css={{
        color: 'green',
        [mq[0]]: {
          color: 'gray'
        },
        [mq[1]]: {
          color: 'hotpink'
        }
      }}
    >
      Some text!
    </div>
    <p
      css={css`
        color: green;
        ${mq[0]} {
          color: gray;
        }
        ${mq[1]} {
          color: hotpink;
        }
      `}
    >
      Some other text!
    </p>
  </div>
)

numbers

import { jsx } from '@emotion/react'

render(
  <div
    css={{
      padding: 8,
      zIndex: 200
    }}
  >
    This has 8px of padding and a z-index of 200.
  </div>
)

fecepaint

npm i facepaint

import { jsx } from '@emotion/react'
import facepaint from 'facepaint'

const breakpoints = [576, 768, 992, 1200]

const mq = facepaint(
  breakpoints.map(bp => `@media (min-width: ${bp}px)`)
)

render(
  <div
    css={mq({
      color: ['green', 'gray', 'hotpink']
    })}
  >
    Some text.
  </div>
)

내가 느낀 장, 단점 😎

장점

혼자 프론트엔드를 개발하고 스타일을 주려면 Emotion을 쓰는 것이 훨씬 편할 겁니다. 왜냐하면 엘리멘트 태그들을 자기 마음대로 커스터마이징 할 수 있고 자기가 작성한 태그니까 알기가 쉬울 것 이라고 생각합니다 🙂

단점

다른 개발자 분이 코드를 봤을 때 2번 일을 해야하는 경우가 있을 수 있습니다. div라는 태그를 바꾸고 싶은데 실제 퍼블리싱 코드에는 div가 없어서 div emotion 코드를 찾아서 변경을 해줘야하는 부분이 단점? 불편한점 이라고 생각합니다. 그리고 엘리먼트 태그들은 많아질 수록 가독성이 떨어진다는 부분이 저에게는 단점으로 느껴졌습니다. 😢

결론

일단은 Emotion으로 되어있는 코드들을 보고 유지보수에 최대한 힘을 쓰는 것이 첫 번째 라고 생각합니다. 물론 다른 스타일 라이브러리나 css 등은 보기 쉽겠지만 조금은 더 시간을 들여야 하지 않나, 이 시간을 최대한 줄이자 라는 마음으로 개발해야 할 것 같습니다. 그리고 조금씩 익숙해지면 바로바로 적응이 가능할 것 같습니다 😊

'React' 카테고리의 다른 글

상태 관리 라이브러리 Recoil 이란?  (0) 2021.12.27
react animation?  (0) 2021.04.22
React Styled Component에 대해 알아보자!  (0) 2019.10.07
canvas 라이브러리 Fabric Demo  (0) 2019.09.20
CORS 란?!  (0) 2019.09.16

Recoil

recoil이란 mobx → redux → recoil 순서대로 페이스북에서 만든 상태 관리 라이브러리이다.angular같은 경우 부모 컴포넌트에서 자식 컴포넌트 사이의 props를 주고받기 편하게 되어있다.주고 받을 수는 있지만 코드가 굉장히 복잡해지고 어디서 어떤 data를 받았는지, 내려줬는지 일일히 다 체크해야한다. 그래서 이 문제를 해결하기위해 상태 관리 라이브러리가 나왔다.

그림1)

첫번째 그림은 최상단 컴포넌트가 X 내려받을 컴포넌트가 Y 라고 가정했을 때

X → Y 로 데이터가 전달 되려면 저렇게 내리고 내리고 내리고... 해야 값을 받을 수 있다

그림2)

하지만 두 번째의 네모 역할이 상태 관리 라이브러리이다. 장바구니, 샵 이라고도 많이 불린다.

필요한 data를 저곳에 넣어 놓고 어디서든 꺼내서 사용하면 된다.

위에 예시는 간단한 구조라 쉽겠지만 트리구조가 길어지면 복잡해져서 거의 99%는 상태 관리 라이브러리를 사용한다고 생각한다. ( 글쓴이의 생각 )

 

 

그럼이제 recoil을 알아보자.

Installation

CRA(npx create-react-app)로 리액트 파일을 생성한 후 아래 cli로 리코일을 설치한다.

npm i recoil // or yarn add recoil

RecoilRoot

리코일 state를 사용하는 컴포넌트들은 <RecoilRoot>를 필요로 한다. <RecoilRoot>를 사용하는 가장 좋은곳은 root component이다.

// app.js
import React from 'react';
import {
  RecoilRoot,
  atom,
  selector,
  useRecoilState,
  useRecoilValue,
} from 'recoil';    // import!!! import하는것은 important하닌까 ~

function App() {
  return (
    <RecoilRoot>    // 이렇게 감싸주어야한다.
      <CharacterCounter />
    </RecoilRoot>
  );
}

Atom

아톰은 상태를 말하며 어떠한 컴포넌트에서 씌여지고 읽혀질 수 있다. 아톰의 va를 읽는 컴포넌트들은 암묵적으로 그 아톰에게 참고(subscribe)되어지고 있다. 그래서 아톰의 업데이트는 이 아톰을 참고하고 있는 컴포넌트를 리렌더 시킨다.

const textState = atom({
  key: 'textState', // unique ID (다른 atoms/selectors을 구별하기 위해서)
  default: '', // default value (aka initial value)
});

function CharacterCounter() {
  return (
    <div>
      <TextInput />
      <CharacterCount />
    </div>
  );
}

function TextInput() {
  const [text, setText] = useRecoilState(textState);

  const onChange = (event) => {
    setText(event.target.value);
  };

  return (
    <div>
      <input type="text" value={text} onChange={onChange} />
      <br />
      Echo: {text}
    </div>
  );
}

Selector

셀렉터는 하나의 추출된 state다. '추출된(derived 또는 파생된)'이라는 의미는 state가 변경(transformation)된것을 의미한다. 다시 말해서 추출된 상태란, 주어진 상태를 변경(modify)시키는 순수 함수에 의해서 변경된 스테이트(the output)라고 말할 수 있다.(실제 상태를 변경시키지는 않는거 같다.)

// usage
const charCountState = selector({
  key: 'charCountState', // unique ID (with respect to other atoms/selectors)
  get: ({get}) => {
    const text = get(textState);

    return text.length;
  },
});

function CharacterCount() {
  const count = useRecoilValue(charCountState);

  return <>Character Count: {count}</>;
}

간단하게

import React from 'react';
import countState from './Atoms';
import { useRecoilState,
  useRecoilValue,
  useSetRecoilState,
  useResetRecoilState
} from 'recoil';

function Counter() {
  const [counter, setCounter] = useRecoilState(countState);
  // useState와 같지만, useRecoilState을 사용하여 다른 파일에 있는 아톰을 읽을 수 있다.
  const currentCount = useRecoilValue(countState);  // 읽기 전용!
  const counterHandler = useSetRecoilState(countState); // 값만 변경 시키기
  const resetCounter = useResetRecoilState(countState); // 디폴트값으로 값 변경

이정도 명령어만 알고있으면 좋을 것 같다.

뒤에 useRecoilState, useRecoilValue useSetRecoilState, useResetRecoilState 정도 알고있자.

recoil은 정말 hooks에 잘 맞게 나온 상태 라이브러리 같다.

 

틀린부분이 있으면 피드백 주시면 감사하겠습니다 :)

'React' 카테고리의 다른 글

Emotion 라이브러리란?  (0) 2021.12.29
react animation?  (0) 2021.04.22
React Styled Component에 대해 알아보자!  (0) 2019.10.07
canvas 라이브러리 Fabric Demo  (0) 2019.09.20
CORS 란?!  (0) 2019.09.16

animation ?

페이지 전환, 스크롤 이벤트, 구성 요소 진입, 종료 등을 처리 할 때 나 그 외 시각적으로 보이는 애니메이션 입니다.

 

현재 2021년 기준 animation의 라이브러리 사용률은

  1. React Spring.
  2. React Circle.
  3. React Particle Effect Button.
  4. React Stonecutter.
  5. Ant Motion.
  6. React Scroll Parallax.
  7. React Loading.
  8. React flip toolkit.

이라고 합니다.

 

그래서 React Spring 라이브러리로 코드를 짜볼겁니다.

 

react-spring.io/

 

react-spring

Bring your components to life with simple spring animation primitives for React

react-spring.io

 

라이브러리 설치

npm install --save react-spring 

 

사이트에 codesandbox.io/embed/n9vo1my91p

 

n9vo1my91p - CodeSandbox

n9vo1my91p by drcmda using lodash-es, react, react-dom, react-scripts, react-spring, react-use-gesture

codesandbox.io

 

이 예시로 코드를 한번 보겠습니다.

import { render } from 'react-dom'
import React, { useRef } from 'react'
import clamp from 'lodash-es/clamp'
import { useSprings, animated } from 'react-spring'
import { useGesture } from 'react-use-gesture'
import './styles.css'

const pages = [
  'https://images.pexels.com/photos/62689/pexels-photo-62689.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260',
  'https://images.pexels.com/photos/296878/pexels-photo-296878.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260',
  'https://images.pexels.com/photos/1509428/pexels-photo-1509428.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260',
  'https://images.pexels.com/photos/351265/pexels-photo-351265.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260',
  'https://images.pexels.com/photos/924675/pexels-photo-924675.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260'
]

function Viewpager() {
  const index = useRef(0)
  const [props, set] = useSprings(pages.length, i => ({ x: i * window.innerWidth, sc: 1, display: 'block' }))
  const bind = useGesture(({ down, delta: [xDelta], direction: [xDir], distance, cancel }) => {
    if (down && distance > window.innerWidth / 2)
      cancel((index.current = clamp(index.current + (xDir > 0 ? -1 : 1), 0, pages.length - 1)))
    set(i => {
      if (i < index.current - 1 || i > index.current + 1) return { display: 'none' }
      const x = (i - index.current) * window.innerWidth + (down ? xDelta : 0)
      const sc = down ? 1 - distance / window.innerWidth / 2 : 1
      return { x, sc, display: 'block' }
    })
  })
  return props.map(({ x, display, sc }, i) => (
    <animated.div {...bind()} key={i} style={{ display, transform: x.interpolate(x => `translate3d(${x}px,0,0)`) }}>
      <animated.div style={{ transform: sc.interpolate(s => `scale(${s})`), backgroundImage: `url(${pages[i]})` }} />
    </animated.div>
  ))
}

render(<Viewpager />, document.getElementById('root'))

 

공식사이트 코드입니다. 참조하면 좋을 것 같습니다.

 

Hooks

useChain - 이전에 정의 된 애니메이션 후크의 실행 순서를 설정합니다. 여기서 하나의 애니메이션은 순서대로 다른 애니메이션이 시작됩니다. 연결하려는 애니메이션에서 참조를 수집해야 애니메이션이 자체적으로 시작되지 않습니다. 순서는 후속 렌더 패스에서 변경할 수 있습니다.

 

useSpring -  값을 애니메이션 값으로 바꿉니다.

 

useSprings - 각각 자체 구성을 가진 여러 스프링을 만듭니다. 정적 목록 등에 사용됩니다.

 

useTrail - 단일 구성으로 여러 스프링을 생성하고 각 스프링은 이전 구성을 따릅니다. staggered animations 에 사용된다고합니다.

 

useTransition - 애니메이션 된 TransitionGroup입니다. items and lifecycles를 추가해야합니다. 항목이 추가되거나 제거 될 때마다 이러한 변경 사항이 애니메이션으로 표시됩니다.

 

공식페이지의 Hooks 부분을 보면 자세히 나와있습니다!

 

import { useSpring, animated } from 'react-spring'

function App() {
  const data = useSpring({ count: 1, from: { count: 0 } })
  // spring을 useSpring 으로 정의합니다
  return <animated.div>{props.number}</animated.div> 
  // animated을 return으로 묶기
}

Hooks api 구조입니다.

Render Props

spring - 값을 애니메이션 값으로 바꿉니다.

 

springContext - 지정된 children에서 애니메이션을 수정 하지만 후크 API (예 useSpring:) 또는 renderprops API (예 :)로 만든 애니메이션 만 <Spring>영향을받습니다. new SpringValue()또는 new Controller()로 생성 된 애니메이션 은 영향을받지 않습니다.

 

trail - 단일 구성으로 여러 스프링을 생성하고 각 스프링은 이전 구성을 따릅니다. staggered animations에 사용하세요

 

transition - 애니메이션 된 TransitionGroup입니다. items and lifecycles를 추가해야합니다. 항목이 추가되거나 제거 될 때마다 이러한 변경 사항이 애니메이션으로 표시됩니다.

 

import { Spring } from 'react-spring/renderprops'

function App() {
  return (
    <Spring from={{ count: 0 }} to={{ count: 1 }}>
      {props => <div>{props.count}</div>}
    </Spring>
  )
}

Render Props api 구조입니다.

두개의 차이

Hooks, Render Props (Class) 둘 중에 자신의 코드에 따라 사용하면 될 것 같습니다.

둘의 가장 큰 차이점은 Hooks는 view를 몰라서 스타일을 스코프 밖에 사용했다면 해당 스타일을 custom hooks를 사용해서 <animated.div />에 알려줘야 합니다.

 

결론

아직 프로젝트에 적용은 안해보고 흥미롭고 나중에 적용해야지 생각하고 정리글을 올려봤습니다. 추가 피드백이나 부족한 부분이 있으면 알려주시면 감사하겠습니다!

 

 

 

 

'React' 카테고리의 다른 글

Emotion 라이브러리란?  (0) 2021.12.29
상태 관리 라이브러리 Recoil 이란?  (0) 2021.12.27
React Styled Component에 대해 알아보자!  (0) 2019.10.07
canvas 라이브러리 Fabric Demo  (0) 2019.09.20
CORS 란?!  (0) 2019.09.16

npm install --save styled-components 를 설치해줍니다.

Babel 플러그인 도 사용하는 것이 좋습니다 (필수는 아님) . 더 읽기 쉬운 클래스 이름, 서버 측 렌더링 호환성, 더 작은 번들 등과 같은 많은 이점을 제공합니다.

 

styled-components 는 태그가 지정된 템플릿 리터럴을 사용하여 구성 요소를 스타일링합니다.

컴포넌트와 스타일 사이의 맵핑을 제거합니다. 이는 스타일을 정의 할 때 스타일이 첨부 된 일반 React 컴포넌트를 실제로 생성한다는 것을 의미합니다.

이 예제는 래퍼와 제목이라는 두 가지 간단한 구성 요소를 작성합니다.

// Create a Title component that'll render an <h1> tag with some styles
const Title = styled.h1`
  font-size: 1.5em;
  text-align: center;
  color: palevioletred;
`;
// Create a Wrapper component that'll render a <section> tag with some styles
const Wrapper = styled.section`
  padding: 4em;
  background: papayawhip;
`;
// Use Title and Wrapper like any other React component – except they're styled!
render(
  <Wrapper>
    <Title>
      Hello World!
    </Title>
  </Wrapper>
);

결과

 

 

출처 : https://www.styled-components.com/docs/basics

'React' 카테고리의 다른 글

상태 관리 라이브러리 Recoil 이란?  (0) 2021.12.27
react animation?  (0) 2021.04.22
canvas 라이브러리 Fabric Demo  (0) 2019.09.20
CORS 란?!  (0) 2019.09.16
React Library 모음  (0) 2019.09.05

+ Recent posts