styled-componentsを使ったReactコンポーネント作成入門

「[ランサーズ Advent Calendar 2018](https://qiita.com/advent-calendar/2018/lancers)」9日目の記事になります。
昨日は、「[ランサーズでインターンをして感じたプログラミング学習における個人とチームの違い](https://engineer.blog.lancers.jp/2018/12/%E3%83%A9%E3%83%B3%E3%82%B5%E3%83%BC%E3%82%BA%E3%81%A7%E3%82%A4%E3%83%B3%E3%82%BF%E3%83%BC%E3%83%B3%E3%82%92%E3%81%97%E3%81%A6%E6%84%9F%E3%81%98%E3%81%9F%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F/)」でした。

ランサーズはいくつかReactのプロダクトがあるのですが、スタイルはcss-modulesを使用して書いていました。
最近Reactの開発にstyled-componentsを採用したところ、非常に書き心地が良かったので紹介します。

# styled-componentsとは

styled-copmonentsはJavaScriptのファイルの中でcssを定義するためのライブラリで、いわゆるCSS in JSの一種です。

styled-componentsはstyled.[タグ名]\`[css]\`でスタイルをあてたコンポーネントを作成することができます。
作成したコンポーネントは変数名をタグ名にして使うことができます。

```jsx:App.jsx
import styled from "styled-components";

const Title = styled.h1`
　　　　font-size: 30px;
  color: red;
`;

const App = () => (
  <Title>タイトル</Title>
)
```

ちなみにstyled.h1にテンプレートリテラル(``)が続いている構文が見慣れないかもしれないですが、これは[タグ付きテンプレートリテラル](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/template_strings)と呼ばれる構文です。

# コンポーネントを作ってみる

styled-componentsを使って下の画像のようなボタンのコンポーネントを作ってみます。

このボタンはdefault・Primary・Dangerの三種類の色があります。
また大きさが2種類、disabledとそうではないパターンが存在します。

![スクリーンショット 2018-12-08 12.12.48.png](https://qiita-image-store.s3.amazonaws.com/0/74937/9a2c4fe6-de93-36ed-19a4-e2fd7163005b.png)

最終的なコードは[こちら](https://codesandbox.io/s/81lo54zk3j)に置いてあります。

## 共通部分をつくる

はじめにdefaultのボタンを作り、すべてのパターンに共通する箇所のスタイルをあてます。
ここでは、フォントのサイズや丸みをつけたり、ホバー時のカーソルがポインターになるようにしています。

```jsx:共通部分をつくる
const Button = styled.button`
  font-size: 1rem;
  padding: 5px 10px;
  border-radius: 5px;
  cursor: pointer;
`;

const App = () => (
  <div>
    <Button>Button</Button>
  </div>
)
```

![スクリーンショット 2018-12-08 11.32.31.png](https://qiita-image-store.s3.amazonaws.com/0/74937/214b92ff-4a90-e5b0-c05b-8098ae8114fd.png)


## 色を変える

次に色を変えてみます。
今回はdefaultとPrimary、Dangerの3タイプのボタンが使えるようにします。

```jsx:色を変える
const Button = styled.button`...`;

const PrimaryButton = styled(Button)`
  border: none;
  color: white;
  background: #007bff;
`;

const DangerButton = styled(Button)`
  border: none;
  color: white;
  background: #dc3545;
`;

const App = () => (
  <div>
    <Button>Button</Button>
    <PrimaryButton>Primary</PrimaryButton>
    <DangerButton>Danger</DangerButton>
  </div>
)
```

`styled([コンポーネント名])`で既存のコンポーネントに対してstyleを追加することができます。
ここではdefaultのbuttonにbackgroudやcolorを追加して新しいコンポーネント`PrimaryButton`,`DangerButton`を作っています。

![スクリーンショット 2018-12-08 11.32.10.png](https://qiita-image-store.s3.amazonaws.com/0/74937/5d6fed5d-4c7d-1b67-d528-db21ea08b91b.png)

## 大きさを変える

defaultのサイズに加えて、大きいサイズのボタンが使えるようにします。
コンポーネントのpropsにlargeを与えることでサイズを変更できるようにします。

```jsx:大きさを変える
const Button = styled.button`
  font-size: 1rem;
  padding: ${props => (props.large ? "10px 15px" : "5px 10px")};
  border-radius: 5px;
  cursor: pointer;
`;

const PrimaryButton = styled(Button)`..`;
const DangerButton = styled(Button)`...`;

const App = () => (
  <div>
    <div>
      ...
    </div>
    <div>
      <Button large>Button</Button>
      <PrimaryButton large>Primary</PrimaryButton>
      <DangerButton large>Danger</DangerButton>
    </div>
  </div>
)
```

cssはテンプレートリテラルを使って記述するので`${}`の中に変数などを入れることができます。
またstyled-componentsで`${}`の中に関数を与えることもでき、第一引数はコンポーネントのpropsになります。

上のコードではpropsにlargeが与えられた場合とそうでない場合で、paddingを変えています。

![スクリーンショット 2018-12-08 11.51.49.png](https://qiita-image-store.s3.amazonaws.com/0/74937/0a65510d-a3e2-8c84-b5f8-597449d39654.png)

## disabledのときにスタイルを変える

ボタンがdisabledのときに色とカーソルの形状を変えます。
大きさを変えるときと同様にpropsにdisabledが与えられたときにスタイルを変えます。

```jsx:disabledのときにスタイルを変える
import styled, { css } from 'styled-components';

const Button = styled.button`
  ...

  ${props => props.disabled && disabledCss}
`;

const disabledCss = css`
  opacity: 0.5;
  cursor: default;
`;

const PrimaryButton = styled(Button)`..`;
const DangerButton = styled(Button)`...`;

const App = () => (
  <div>
    ...
    <div>
      <div>
        <Button disabled>Button</Button>
        ...
      </div>
      <div>
        <Button large disabled>Button</Button>
        ...
      </div>
    </div>
  </div>
)
```

css``はcssを変数化して、そのcssをテンプレートリテラル内に挿入したい場合に使います。
ここではdisabledの場合のスタイルを変数化して、propsのdisabedが存在した場合に挿入しています。

これで完成です。

![スクリーンショット 2018-12-08 12.12.48.png](https://qiita-image-store.s3.amazonaws.com/0/74937/9a2c4fe6-de93-36ed-19a4-e2fd7163005b.png)

# css-modulesとの比較

同じものをcss-modulesで作ろうとすると下のようになると思います。

```jsx:css-modulesの場合
import React from 'react';
import styles from './styles.css';

function createButton(type) {
  return ({children, large, disabled}) => {
    const classNameList = [styles.button, styles[type]]
    if (large) {
      classNameList.push(styles.large)
    }
    if (disabled) {
      classNameList.push(styles.disabled)
    }
    return (
      <button className={[styles.button]}>{children}</button>
    )
  }
}

const Button = createButton('default');
const PrimaryButton = createButton('primary');
const DangerButton = createButton('danger');
```

比較をするとstyled-componentsには以下のような利点があると考えます。

- styled-componentsの中だけで完結する。簡単なコンポーネントであればreactをimportする必要すらない。
- css-modulesでスタイルを変えることは最終的にclassNameに与える文字列をどうやって生成するかに帰着する。そのためこのような簡単なコンポーネントでも複雑なロジックが発生する。styled-componentでは複雑化しない。
- 一つのファイルで完結するので見通しが良い。

# まとめ

styled-componentsを使ってボタンのコンポーネントを作成しました。
css-modulesと比較をすると簡潔なコードでコンポーネント作成ができていることがわかると思います。

以上、「[ランサーズ Advent Calendar 2018](https://qiita.com/advent-calendar/2018/lancers)」9日目の記事でした。

明日は「ランサーズエンジニアのキーボードの世界」です。お楽しみに！