[React] styled-component を利用する際の最初に知っておきたいことまとめ

## はじめに
これまでVuejsで開発をしてきたのですが、新しいプロジェクトでReactを利用することになりました。
キャッチアップのために、CSS in JS の代表的なライブラリの1つである```styled-components```を利用して簡単なWebサイトを作成しました。

実装を進める中で自分が調査して、なるほどと思ったポイントをまとめました。

**[作成したサイト]**

https://tdee-keisan.com/

**[Github]**

https://github.com/saba-can00/tdee_calculator_web

## CSS in JS について

### Reactのスタイリングの方法について

CSS in JS について説明する前にまず基本として、Reactでは要素をどのようにスタイリングするかについて記載します。

Reactでは、要素のスタイリングに下記の2つの方法を用意しています。
 
1. 通常のHTMLでスタイリングをするようにcssファイルを別に指定して、classNameプロパティに指定する
2. Reactエレメントのstyleプロパティにcssに解釈できるオブジェクトを渡して、スタイリングを行う

React公式では、パフォーマンスの観点から1の方法を推奨しています。

https://ja.reactjs.org/docs/faq-styling.html#are-inline-styles-bad
 
この公式の見解をそのまま当てはめるとcssファイルでスタイリングは定義したほうがいいとなります。
 
ただしCSSはグローバルスコープなので、比較的大きめのアプリケーションを実装する場合には名前の衝突やスタイルの影響範囲などをどのように担保するかの解決方法を検討する必要があります。

### CSS in JS をなぜ利用するか

CSS in JS は、cssの定義をjsファイルに記載して、上記の2のようにインラインスタイルで定義することでcssのスコープをそのコンポーネントに閉じようというコンセプトでスタイリングを行う手法です。

Facebookの@Vjeuxさんの「React: CSS in JS」 でこの手法が有名になったようで現在では様々なライブラリが実装されています。

https://speakerdeck.com/vjeux/react-css-in-js?slide=38

CSS in JS を採用する理由は、主に下記です。

* cssのスコープを対象のコンポーネントに閉じることでスタイル崩れを心配することなく開発したい
* JSXで記載したHTMLの構造とcssのスタイルを一緒に管理できる
* JSの既存のツールを利用して、静的解析やコードの入力補完がしやすい

### [CSS in JS の各ライブラリ]

2021年 CSS in JS のライブラリはかなり色々と出ています。

* Emotion
* styled-components
* JSS
* glamorous
* Linaria
* CSS Modules
 
などがざっと調べただけでも出てきます。
 
https://2020.stateofcss.com/ja-JP/technologies/css-in-js/


npm trend を確認すると、emotion が一番人気ですが、jss / styled-components もかなり近いレベルで採用されているようです。

https://www.npmtrends.com/@emotion/css-vs-glamorous-vs-jss-vs-linaria-vs-styled-components-vs-css-modules-loader-core

最初はemotionを利用してみようと思ったのですが、css記法を利用する際には [JSX Pragma](https://emotion.sh/docs/css-prop#jsx-pragma)の対応が必要とのことでまずはReactに慣れることを優先して簡単に導入できるstyled-componentsを採用しました。

(v4以降ではstyled-componentsでもcss propに対応したため、css propを利用したいという場合でもstyled-componentsで対応できるようになりました。)
 
### styled-componentsでできること

styled-componentsはReactコンポーネントをCSSでスタイルすることが主な目的なのですが、下記のように様々なスタイリングのパターンに対応しています。

* コンポーネントに共通したテーマの設定([ThemeProvider](https://styled-components.com/docs/advanced#theming)を利用) 
* 既存コンポーネントへのスタイルの適用(参考:[Styling any component](https://styled-components.com/docs/basics#styling-any-component) )
* 擬似クラス / 擬似要素の適用(参考:[Pseudoelements, pseudoselectors, and nesting](https://styled-components.com/docs/basics#pseudoelements-pseudoselectors-and-nesting) )
* React Nativeでの利用(参考: https://styled-components.com/docs/basics#react-native)

### styled-componentsの使い方をざっくり知りたいという入門者向けの情報

まずは、ざっくりstyled-componentsの使い方が知りたいという方は下記のリンクが参考になります。

公式サイト：
https://styled-components.com/docs/basics#motivation

styled-componentsの使い方の初歩を丁寧に解説してくれている
https://www.webprofessional.jp/style-react-components-styled-components/

styled-componentsの基本的な使い方、Theme, Global
https://akabeko.me/blog/2020/11/styled-components/

### 比較的新しめのstyled-componentsの機能の紹介

個人的にこんなことまでできるんだと驚いた最後に比較的新しめの機能を3つ紹介させていただきます。

1. ポリモーフィズムによるHTMLタグの動的な切り替え
2. css prop への対応
3. transient props

#### 1. ポリモーフィズムによるHTMLタグの動的な切り替え

これまでは`styled`の適用をする際にラップするHTML要素を決めなれけばならず、cssは同じだけどタグだけ変更したいとかpropsによって動的にHTMLの属性を変更したいというケースでは対応が難しい状態でした。

しかし、この機能を利用すれば、HTMLの属性をas propsによってrenderする際に決めることができます。
(乱用すると定義した場所でのタグの意味がなくなるので、注意は必要です。)

データによって表示を切り替える必要のある複雑なリストやグリッドを実装するときには重宝すると思います。
 
```react
// as propsを利用した例:
import styled from "styled-components";

const Component = styled.div`
  color: red;
`;

render(
  <Component
    as="button"
  >
    Hello World!
  </Component>
)
 
```

上記のJSXは下記のように`button`タグに解釈されます。

```html
// render されたHTML
<button class="sc-bqyKva ehfErK">Hello World!</button>
 
```

#### 2. css prop への対応

以前はstyled-composentは`css prop`に対応できておらず、必ずスタイリング対象のHTMLタグが必要となっていたため無駄なDOMが構築されてしまうケースがありました。
 
CSS in JS系の有名なライブリとしてはemotionは`css prop`に対応できていたため、`css prop` が利用したい場合はstyled-composentを採用せず、emotionを利用することが多かったようです。

`css prop`に対応できたことで今後はstyled-composentが採用されることも増えるかと思います。


補足ですが、Typescriptで利用する場合には、下記の記載を追加しないと型エラーが出るようなのでご注意ください。

```typescript
import {} from 'styled-components/cssprop'
```

#### 3. transient props 

styled-composentで既存コンポーネントのスタイリングをpropsを利用して行う場合に、propsが意図せず伝播してしまうことがありました。

少しわかりにくいので、例で説明します。

下記のように普通にスタイリングを行うと、Containerコンポーネントにのみprops.colorを適用させたい場合でもTextにまでprops.colorが伝播されてしまいます。

```react
// 普通にスタイリングした場合
import styled from "styled-components";
const Text = props => <p {...props} />;
const Container = styled(Text)`
  color: ${p => p.color};
`;
// colorがTextにまで伝播してしまう
<Container color="blue">Hello world!</Container>
```

この例のように自作コンポーネントの場合は、注意して実装すれば問題ないのですが、ライブラリなどで実装されたコンポーネント(例えば react-routerの<Link>など) は自分でpropsを設定できないので伝播させたくない場合に困ってしまいます。

このような場合には、対象のpropsに`$`を利用することでpropsを伝播させなくするのが`transient props `の機能です。

```react
// 普通にスタイリングした場合
import styled from "styled-components";
const Text = props => <p {...props} />;
const Container = styled(Text)`
  color: ${p => p.$color};
`;
// colorはContainer
<Container $color="blue">Hello world!</Container>
```


transient props の詳細な仕様については下記の記事が詳しいので、気になる方はご参照ください。

https://medium.com/@probablyup/introducing-transient-props-f35fd5203e0c

## 参考リンク
* [React公式: CSS とスタイルの使用](https://ja.reactjs.org/docs/faq-styling.html#are-inline-styles-bad)
* [CSS Modules 所感](https://morishitter.hatenablog.com/entry/2015/09/28/103334)
* [食べログ フロントエンドエンジニアブログ:SassからCSS Modules、そしてstyled-componentsに乗り換えた話](https://note.com/tabelog_frontend/n/n2541778b81e3#ZwKJX)
* [クックパッド開発者ブログ:レシピサービスのフロントエンドに CSS in JS を採用した話](https://techlife.cookpad.com/entry/2021/03/15/090000)
* [Emotion > css props > JSX Pragma](https://emotion.sh/docs/css-prop#jsx-pragma)
* [styled-component > API Reference > "as" polymorphic prop](https://styled-components.com/docs/api#as-polymorphic-prop)
* [styled-component > API Reference > Transient props](https://styled-components.com/docs/api#transient-props)
* [Evan Jacobs: Introducing “transient” props](https://medium.com/@probablyup/introducing-transient-props-f35fd5203e0c)
* [Github:[styled-component] Support css prop](https://github.com/DefinitelyTyped/DefinitelyTyped/issues/31245)