Reactでstyled-componentsを使う

# 前書き
ReactでTodoを作るにあたってstyled-componentsでいろいろ試行錯誤しながら試してみたので、まとめてみます。
# 使ってみた感想
まず、スタイルを当てたい要素一つ一つに別々のクラス名を考えなくてもよくなったため、名前を考える負担がとても減りました。また、JavaScriptであるので、色などを定数として管理しておくことができ、とても便利でした。SCSSのようにネスト構造でスタイルを書くことができるのも、個人的には好きでした。

では、Todoを作るにあたって、自分がstyled-componentsを使って便利だなぁと思ったものを紹介していきたいと思います。

# 目次
1. ネスト構造を上手に使い、少ないコンポーネントで表現する！
2. cssで表現できることはstyled-componentsで表現できる！
3. propsを用いて、それぞれに別のスタイルを当てる！
4. 3.より簡単に、カスタムデータ属性を使って表現する！

## まず、準備
styled-componentsをインストールします。

```
npm install --save styled-components
yarn add styled-components
```
では、みていきます！
# 1. ネスト構造を上手に使い、少ないコンポーネントで表現する！
まず、作成したものがこちらです。
![スクリーンショット 2019-05-17 23.44.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405522/67f8c480-f961-373c-cdf6-aa7b267cf09c.png)
そして、ファイルの中身がこちらです。

```index.js
import React, { Component } from 'react';
import styled from 'styled-components';

class App extends Component {
  render() {
    return (
      <Container>
        <div>
          <h1>Title</h1>
          <p>Lorem ipsum dolor sit （省略） anim id est laborum.</p>
          <div>
            <button>NO</button>
            <button>YES</button>
          </div>
        </div>
      </Container>
    );
  }
}

const Container = styled.div`
  width: 100vw;
  height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  div{
    width: 40%;
    h1{
      color: #646464;
    }
    p{
      color: #646464;
    }
    div{
      width: 100%;
      display: flex;
      justify-content: space-between;
      margin-bottom: 2rem;
      button{
        font-size: 1rem;
        box-sizing: border-box;
        width: 45%;
        padding: 10px;
        background: #f5f5f5;
        color: #646464;
        border: 1px solid #666;
      }
    }
  }
`;

export default App;
```
この例では、Containerコンポーネントだけをstyled-componentsで作成し、その子要素をネスト構造でスタイルを当てていっています。より要素が多くなって複雑になった時に、大枠の部分をContainerとして、コンテナごとにコンポーネントを作成しておくと、どのコンテナに入っている要素かがわかりやすくてオススメです。（今回の例ではdivが二つ出てくるので、こういった場合は区別したほうがいいと思います...）

# 2. cssで表現できることはstyled-componentsで表現できる！
CSSでコーディングをすることに慣れている人が思うことが、「擬似要素とか使えるん？？」ってことだと思います。
普通に使えます！！
その例がこちらです。(上の例と同じコードなのでContainerコンポーネントのみ書きます)

```index.js
const Container = styled.div`
  width: 100vw;
  height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  div{
    width: 40%;
    h1{
      color: #646464;
      text-align: center;
      position: relative;
      letter-spacing: 2px;
      //擬似要素
      ::after{
        content: '';
        display: inline-block;
        height: 4px;
        width: 25px;
        background: #646464;
        border-radius: 4px;
        position: absolute;
        bottom: -6px;
        left: 50%;
        transform: translateX(-50%);
      }
    }
    p{
      color: #646464;
    }
    div{
      width: 100%;
      display: flex;
      justify-content: space-between;
      margin-bottom: 2rem;
      flex-wrap: wrap;
      button{
        font-size: 1rem;
        box-sizing: border-box;
        width: 45%;
        padding: 10px;![スクリーンショット 2019-05-18 1.13.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405522/de114daa-9a78-93e7-fe43-00db5360270f.png)

        background: #f5f5f5;
        color: #646464;
        border: 1px solid #666;
　　　　　//擬似クラス
        :hover{
          background: #d2d2d2;
          cursor: pointer;
        }
　　　　　//メディアクエリ
        @media (max-width: 575.98px) {
          width: 100%; 
          margin: 1rem 0;
        }
      }
    }
  }
`;
```
完成したのがこの画像です。
![スクリーンショット 2019-05-18 0.07.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405522/4777739e-ecb0-4857-b337-75bc352c2f8c.png)
<img width="414" alt="localhost_3001_(iPhone 6_7_8 Plus).png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405522/25389781-3307-cdce-aa04-f3b5fb273cac.png">
コードをみていただくと、やってること自体はわかると思うので説明は割愛します。1枚目の画像が、PCサイズの時の見た目で、右のボタンにホバーしています。2枚目は、スマホサイズでみたもので、ボタンの配置が調整されています。

次に、もう一つ例を出します。まず、画像を見てください。
![スクリーンショット 2019-05-18 1.13.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405522/621822e5-5f0a-dd81-f727-6dacae223c5a.png)
![スクリーンショット 2019-05-18 1.13.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405522/889b1d4a-d5d1-122e-fe81-24bab53f38b9.png)
これはカードになっており、カードをホバーすると、ボタンの透明度(opacity)が０から１になります。2枚目がホバーした状態です。今回はコンポーネントをContainerとButtonを作っています。それでは、コードです。

```index.js

class App extends Component {

  render() {
    return (
      <Container>
        <div className="card">
          <h1>hover</h1>
          <Button>Button</Button>
        </div>
      </Container>
    );
  }
}

const Button = styled.button`
  width: 90%;
  box-sizing: border-box;
  padding: 10px;
  margin: 2rem 0;
  background: #f5f5f5;
  border: solid 1px #666;
  font-size: 1rem;
  opacity: 0;
  transition: opacity .4s ease-in-out;
`;
const Container = styled.div`
  width: 100vw;
  height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #f0f0f0;
  div.card{
      width: 40%;
      background: white;
      text-align: center;
      box-shadow: 4px 4px 10px rgba(0, 0, 0, .2);
      h1{
        color: #646464;
      }
      :hover{
        cursor: pointer;
        ${Button}{
          opacity: 1;
        }
      }
  }
`;
```
この例では、Buttonコンポーネントを先にまず定義しておくことで、次のContainerコンポーネントの中で、Buttonコンポーネントを利用できるようになっています。
それによって、「div.card」がホバーされたら、「div.cardの子要素のButtonコンポーネント」の透明度が１になる、ということを表現できました。

もちろん、Containerコンポーネントの中に全てまとめることはできますが、今回の例では、ボタンの基本のスタイルと、ホバー前後のボタンのスタイルの、２つを書かないといけないため、わかりやすくするために、ボタン自体のスタイルは、それ単体のコンポーネント（今回では、Buttonコンポーネント）で書きました。

「cssで表現できることはstyled-componentsで表現できる！！」のが伝わったでしょうか。

# 3. propsを用いて、それぞれに別のスタイルを当てる！
同じ要素が複数あって、全く同じではないけどほぼ似てるデザインだ！っというときに、CSSでは、共通のクラス名とそれぞれにスタイルをあてるためのクラス名を与えていたと思います。
styled-componentsでは、propsで分岐させることでそれぞれにスタイルを指定することができます。
例えば、以下のようなデザインがあったとします。
![スクリーンショット 2019-05-18 1.51.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405522/c6747088-2cec-b651-c40a-81d8c1bb80bf.png)
三つのボタンで、違うのは、色だけです。そこで、ボタン自体は、同じコンポーネントを用いて、スタイルをあてて、色のみpropsで条件を分けた先でスタイルを指定します。

```index.js
import React, { Component } from 'react';
import styled, { css } from 'styled-components';

const Color = {
  red: "#f00",
  green: "#0f0",
  blue: "#00f",
}

class App extends Component {

  render() {
    return (
      <Container>
        <div>
          <h1>VariousColors</h1>
          <Button red>Button</Button>
          <Button green>Button</Button>
          <Button blue>Button</Button>
        </div>
      </Container>
    );
  }
}

const Button = styled.button`
  width: 90%;
  box-sizing: border-box;
  padding: 20px;
  margin: 2rem 0;
  background: #f5f5f5;
  border: none;
  font-size: 1rem;
  color: white;
  ${props =>
    props.red &&
    css`
      background: ${Color.red};
  `};
  ${props =>
    props.green &&
    css`
      background: ${Color.green};
  `};
  ${props =>
    props.blue &&
    css`
      background: ${Color.blue};
  `};

`;
const Container = styled.div`
  width: 100vw;
  height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  background: white;
  div{
      width: 40%;
      background: white;
      text-align: center;
      box-shadow: 4px 4px 10px rgba(0, 0, 0, .2);
      h1{
        color: #646464;
      }
  }
`;

export default App;
```
先程までの違いとしましては、JSXタグに、<Button red>などのようにプロパティが追加されています。そして、styled-componentsから{ css }をimportすることでコンポーネントの中で使用できるようにしています。

また、今回は```background: ${Color.red};```のように、色を定数としColorという関数でまとめて管理しています。たくさんファイルが増えていくと、よく使う文字色や背景色などを別ファイルでまとめて管理してそれをimportするだけにしておくと、コードが見やすくなりますし、色の修正も元を変えるだけで済むので楽ですね。

# 4. 3.より簡単に、カスタムデータ属性を使って表現する！
カスタムデータ属性は、HTML５で導入されたもので、その要素に属性を持たせることができます。マークアップの際も```<li data-menu="hot">ホットコーヒー</li>```のように、```data-```で始めることで用います。これを用いて先ほどのコードをより簡単にまとめたのがこちらです。

```index.js
import React, { Component } from 'react';
import styled from 'styled-components';

const Color = {
  pink: "#f8aba6",
  black: "#000",
  orange: "#f58220",
}

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      colors: ["pink", "black", "orange"]
    }
  }

  render() {
    return (
      <Container>
        <div>
          <h1>VariousColors</h1>
          {this.state.colors.map((color, idx) => {
            return <Button data-color={color} key={idx} >{color}</Button>
          })}
        </div>
      </Container>
    );
  }
}

const Button = styled.button`
  width: 90%;
  box-sizing: border-box;
  padding: 20px;
  margin: 2rem 0;
  background: #f5f5f5;
  border: none;
  font-size: 1rem;
  color: white;
  &[data-color='pink']{
    background: ${Color.pink};
  }
  &[data-color='black']{
    background: ${Color.black};
  }
  &[data-color='orange']{
    background: ${Color.orange};
  }

`;
const Container = styled.div`
  width: 100vw;
  height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  background: white;
  div{
      width: 40%;
      background: white;
      text-align: center;
      box-shadow: 4px 4px 10px rgba(0, 0, 0, .2);
      h1{
        color: #646464;
      }
  }
`;

export default App;
```
色をデータとして分類して、せっかくなのでReactらしくその色の文字をstateに配列として持たせて、それをmapメソッドで回してみました。3.では{ css }をimportしないといけなかったですが、その必要もありません。
完成したものがこちらです。
![スクリーンショット 2019-05-18 10.31.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405522/a375747e-52a1-03fc-ba67-914bd2329baa.png)
以上になります。

# 後書き
補足的になるのですが、構成について少し書きたいと思います。
styled-componentsは、基本、JSXと同じファイルに書くほうが個人的にオススメですが、ファイルが長くなるのが好ましくない人もいるかもしれません。
その場合は、styled-componentsもコンポーネントなので、外部ファイルからexportするといいかもしれません。ファイル構成の一例として、

```
List
├── index.js
└── style.js
```
が挙げられます。
以上です。ありがとうございました。