React

endsoul·2020년 12월 21일
0

설정

Strict 모드

항상 사용하도록 설정한다.
자손들에 대한 부가적인 검사와 경고를 활성화한다.
개발 모드에서만 활성화되기 때문에 프로덕션 빌드에는 영향을 끼치지 않는다.

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById("root"),
);

Absolute Path Import 설정

jsconfig.json(타입스크립트를 사용하면 tsconfig.json)

{
  "compilerOptions": {
    "baseUrl": "src"
  },
  "include": ["src"]
}

위 설정은 tsc 컴파일러 또는 IDE 에서 모듈 경로를 찾을 때 사용한다. 그래서 webpack 을 실행하면 모듈 경로를 찾을 수 없다고 에러가 발생한다.

webpack.config.js
modules 에 src 경로를 추가한다.

resolve: {
  // 소스 파일에서 모듈 import 를 할 때 탐색할 디렉터리 목록
  modules: ["node_modules", "src"],
  // 소스 파일에서 모듈 import 를 할 때 찾을 파일 확장자 목록
  extensions: [".tsx", ".ts", ".jsx", ".js"],
}

따라서 tsc, IDE, webpack 을 위해서 두 설정을 모두 적용한다.

import UserList from "components/user/UserList";
import { getAllStudent } from "apis/Student";

Typescript

Stateless Functional Component 선언

16.8 버전 이전 (이후 버전에서는 deprecated)

const Card: React.SFC<CardProps> = (props) => {
  return (
    <div />
  )
}

const Card: React.StatelessComponent<CardProps> = (props) => {
  return (
    <div />
  )
}

SFC 는 StatelessComponent 의 별칭이다

16.8 버전 이후

const Card: React.FC<CardProps> = (props) => {
  return (
    <div />
  )
}

const Card: React.FunctionComponent<CardProps> = (props) => {
  return (
    <div />
  )
}

FC 는 FunctionComponent 의 별칭이다

type vs interface

React props, state 에는 type 을 사용하고
라이브러리, 서드파티의 public API 에는 interface를 사용하는걸 추천한다.

type CardProps = {
  no?: number;
  title: string;
  body: string;
}

const Card: React.FunctionComponent<CardProps> = {
  return ({no, title, body}) => {
    return (
      <div>
        {no &&
          <h5>{no}</h5>
        }  
        <div>{title}</div>
        <p>{body}</p>
      </div>
  )
}

Styled-Components

Global CSS 적용

createGlobalStyle 함수가 StyledComponent 를 리턴한다.
head 태그에 inline CSS를 추가한다. 그래서 컴포넌트를 DOM TREE 에서 어느 위치에 넣든 모든 컴포넌트에 CSS를 적용한다.

import { createGlobalStyle } from "styled-components";

type GlobalStyleProps = {
  whiteColor: boolean;
};

const GlobalStyle = createGlobalStyle<GlobalStyleProps>`
  body {
    color: ${(props) => (props.whiteColor ? "white" : "blue")};
  }
`;

ReactDOM.render(
  <React.StrictMode>
    <GlobalStyle whiteColor={false} />
    <App />
  </React.StrictMode>,
  document.getElementById("root"),
);

결과 HTML

<head>
  <title>Cooker</title>
  <meta charset="utf-8">
  <script defer="" src="bundle.js"></script>
  <style data-styled="active" data-styled-version="5.3.3">body{color:blue;}</style><style type="text/css">
  ...

컴포넌트 CSS

const Button = styled.button`
  background: transparent;
  border-radius: 3px;
  border: 2px solid palevioletred;
  color: palevioletred;
  margin: 0 1em;
  padding: 0.25em 1em;
`
...

return (
  <Button>버튼</Button>
)

결과 HTML

<head>
  <style data-styled="active" data-styled-version="5.3.3">
    .fPFEqd{background:transparent;border-radius:3px;border:2px solid palevioletred;color:palevioletred;margin:0 1em;padding:0.25em 1em;}
  </style>
</head>

<button class="App__Button-sc-7xs5z8-0 fPFEqd">버튼</button>

head태그의 inline CSS에 새로운 CSS classname(fPFEqd)을 추가한다. 그리고 버튼에 두 개의 CSS classname(App__Button-sc-7xs5z8-0, fPFEqd)이 적용되어 있다.

중복되지 않는 유니크한 클래스 이름을 생성해서 사용한다. 이런 CSS 적용 방법 때문에 컴포넌트 마다 자신의 독립적인 CSS 를 사용할 수 있다.

속성 값에 따라서 CSS 적용

const Button = styled.button<{
  color: text;
}>`
  color: ${(props) => props.color};
`;

const App: React.FC = () => {
  const [color, setColor] = useState<string>("red");

  const handleClick = () => {
    if (color == "red") {
      setColor("green");
    } else if (color == "green") {
      setColor("blue");
    } else {
      setColor("red");
    }
  };
  
  return (
    <>
      <Button color={color} onClick={handleClick}>
        Toggle
      </Button>
    </>
  );
};

css`` 대신 ``만 사용하면 IDE에서 CSS 문법 검사 등을 하지 않는다. 그래서 css``를 사용하는걸 추천한다.
버튼을 누를 때마다 속성 값이 바뀌고 head태그의 inline CSS에 동적으로 style classname을 생성한다. 만약 동일한 속성 값을 사용하는 classname이 있다면 새로 생성하지 않고 기존 classname을 재사용한다.

최초 HTML(속성 값 "red")

<style data-styled="active" data-styled-version="5.3.3">
  .hXksYn{background:transparent;border-radius:3px;border:2px solid palevioletred;margin:0 1em;padding:0.25em 1em;color:red;}
</style>

버튼 클릭(속성 값 "green")

<style data-styled="active" data-styled-version="5.3.3">
  .hXksYn{background:transparent;border-radius:3px;border:2px solid palevioletred;margin:0 1em;padding:0.25em 1em;color:red;}
  .bZKDlr{background:transparent;border-radius:3px;border:2px solid palevioletred;margin:0 1em;padding:0.25em 1em;color:green;}
</style>

버튼 클릭(속성 값 "blue")

<style data-styled="active" data-styled-version="5.3.3">
  .hXksYn{background:transparent;border-radius:3px;border:2px solid palevioletred;margin:0 1em;padding:0.25em 1em;color:red;}
  .bZKDlr{background:transparent;border-radius:3px;border:2px solid palevioletred;margin:0 1em;padding:0.25em 1em;color:green;}
  .cDwOze{background:transparent;border-radius:3px;border:2px solid palevioletred;margin:0 1em;padding:0.25em 1em;color:blue;}
</style>

세 가지 속성 값에 해당하는 classname 세 개가 만들어 졌다.
버튼 클릭(속성 값 "red")

<style data-styled="active" data-styled-version="5.3.3">
  .hXksYn{background:transparent;border-radius:3px;border:2px solid palevioletred;margin:0 1em;padding:0.25em 1em;color:red;}
  .bZKDlr{background:transparent;border-radius:3px;border:2px solid palevioletred;margin:0 1em;padding:0.25em 1em;color:green;}
  .cDwOze{background:transparent;border-radius:3px;border:2px solid palevioletred;margin:0 1em;padding:0.25em 1em;color:blue;}
</style>

속성 값 "red"에 해당하는 classname이 이미 있기 때문에 새로운 clssname을 만들지 않고 기존 것을 재사용한다.

CSS 믹스인

CSS를 재사용 할 수 있다.

const complexMixin = css<{ yellowColor: boolean }>`
  color: ${(props) => (props.yellowColor ? "yellow" : "green")};
`;

const Button = styled.button<{
  complex: boolean;
  yellowColor: boolean;
}>`
  ${(props) => (props.complex ? complexMixin : "color: blue;")};
`;

return (
  <Button complex={true} yellowColor={false}>
    버튼
  </Button>
)

0개의 댓글