LoginSignup
13
18

More than 3 years have passed since last update.

styled-componentsのcss propを、create-react-appで作成したReact & TypeScriptの環境で動かすためのチュートリアル

Last updated at Posted at 2020-03-09

styled-componentsにはv4からcss propという機能があり、これを用いるとちょっとしたスタイルの修正を、コンポーネントの作成無しで実施できる。

しかしこのcss prop, どうも一筋縄では動かないらしい。特にTypeScript環境では以下のようにハマる人も多いようだ。

https://github.com/DefinitelyTyped/DefinitelyTyped/issues/31245
https://github.com/styled-components/styled-components/issues/2528

私もこの点で大ハマりし半日を潰す羽目になった。
ネット上の記事には断片的な情報しか無く、同じハマり方をする人もいると思うので、備忘録も兼ねて記録を残す。

私の知識が足りないだけなのだと思うが、この件ではネットの記事を見ながら「せめて何処に書くか位ちゃんと書けや!!!」とか「ここまで書いてあってなんであの情報ないんだよ!(バージョン差分の影響かもしれない)」と感じたことが多々あったので、本記事ではチュートリアル形式で、可能な限り再現しやすい形をとる。

具体的にはcreate-react-appでReactアプリを作成するところから、styled-componentsをインストールし、css propの機能によりスタイルを適用するまでの手順を示す。同じ悩みにぶち当たった人の助けになれば。

前提

本チュートリアルは以下の環境を前提とする
- react: 16.13.0
- create-react-app: 3.3.0
- styled-components: 5.0.1

おそらくこの手順は "create-react-app"を用いた環境でのみ有効。

css prop動作確認手順

create-react-app

--typescriptオプションを使用する

$ create-react-app trycssprop --typescript

styled-componentのインストール

$ yarn add styled-components

型定義ファイルも同時にインストールする

$ yarn add @types/styled-components

yarn start でアプリが正常にビルドされることを確認

$ yarn start

スクリーンショット 2020-03-09 19.54.04.png

styled-componentの基本動作確認

スタイルを付加したコンポーネントを作成する。

diff --git a/src/App.tsx b/src/App.tsx
index a53698a..df0b1c0 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,6 +1,11 @@
 import React from 'react';
 import logo from './logo.svg';
 import './App.css';
+import styled from 'styled-components'
+
+const Test = styled.div`
+  color: red;
+`

 function App() {
   return (
@@ -18,6 +23,7 @@ function App() {
         >
           Learn React
         </a>
+        <Test> TEST!!!!!! </Test>
       </header>
     </div>
   );

スクリーンショット 2020-03-09 20.00.03.png

ここからが本題! css propを用いてスタイルを当てる

まず何もせずにcss propを使ってみる。任意のコンポーネントにcss="**some style**"の形でpropを設定することで、スタイルを適用できる。
https://styled-components.com/docs/api#css-prop

diff --git a/src/App.tsx b/src/App.tsx
index df0b1c0..f057b5c 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -23,7 +23,7 @@ function App() {
         >
           Learn React
         </a>
-        <Test> TEST!!!!!! </Test>
+        <Test css="border-bottom: solid 5px white"> TEST!!!!!! </Test>
       </header>
     </div>
   );

ビルドすると以下のようなエラーが表示される。
これはcss="**some style**"がJSXの拡張であり、現状の設定ではBabelが読み取れないことに起因する(らしい)。

Failed to compile.

trycssprop/src/App.tsx
TypeScript error in trycssprop/src/App.tsx(26,15):
No overload matches this call.
  Overload 1 of 2, '(props: Pick<Pick<Pick<DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "style" | "title" | ... 252 more ... | "onTransitionEndCapture"> & { ...; }, "style" | ... 254 more ... | "onTransitionEndCapture"> & Partial<...>, "style" | ... 254 more ... | "onTransitionEndCapture"> & { ...; } & { ...; }): ReactElement<...>', gave the following error.
    Type '{ children: string; css: string; }' is not assignable to type 'IntrinsicAttributes & Pick<Pick<Pick<DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "style" | ... 253 more ... | "onTransitionEndCapture"> & { ...; }, "style" | ... 254 more ... | "onTransitionEndCapture"> & Partial<...>, "style" | ... 254 more ... | "onTransitionEndCapture"> & { ...; } & { ...; }'.
      Property 'css' does not exist on type 'IntrinsicAttributes & Pick<Pick<Pick<DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "style" | ... 253 more ... | "onTransitionEndCapture"> & { ...; }, "style" | ... 254 more ... | "onTransitionEndCapture"> & Partial<...>, "style" | ... 254 more ... | "onTransitionEndCapture"> & { ...; } & { ...; }'.
  Overload 2 of 2, '(props: StyledComponentPropsWithAs<"div", any, {}, never>): ReactElement<StyledComponentPropsWithAs<"div", any, {}, never>, string | ... 1 more ... | (new (props: any) => Component<...>)>', gave the following error.
    Type '{ children: string; css: string; }' is not assignable to type 'IntrinsicAttributes & Pick<Pick<Pick<DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "style" | ... 253 more ... | "onTransitionEndCapture"> & { ...; }, "style" | ... 254 more ... | "onTransitionEndCapture"> & Partial<...>, "style" | ... 254 more ... | "onTransitionEndCapture"> & { ...; } & { ...; }'.
      Property 'css' does not exist on type 'IntrinsicAttributes & Pick<Pick<Pick<DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "style" | ... 253 more ... | "onTransitionEndCapture"> & { ...; }, "style" | ... 254 more ... | "onTransitionEndCapture"> & Partial<...>, "style" | ... 254 more ... | "onTransitionEndCapture"> & { ...; } & { ...; }'.  TS2769

    24 |           Learn React
    25 |         </a>
  > 26 |         <Test css="border-bottom: solid 5px white"> TEST!!!!!! </Test>
       |               ^
    27 |       </header>
    28 |     </div>
    29 |   );

型定義ファイルへのimportの追加

https://github.com/DefinitelyTyped/DefinitelyTyped/issues/31245
の記載を参照して、

import {} from 'styled-components/cssprop'を型定義ファイルに追記する。

なぜ型定義ファイルに上記のimportを追記するのか? これに関しては大変申し訳ないのだが、私自身理解していない。
試行錯誤の結果、型定義ファイルに記載すれば良さそうということだけ分かった。

この辺りが気になる方は、「styled-components css props typescript」あたりでググってみてほしい。
いくつかの記事から、この推論にたどり着くことがなんとなく理解いただけると思う。
もしくはこの理由をご存知の方は、是非ともコメントをいただきたい。

当初'node_modules/@types/react/index.d.ts'に追記していたが、
gitで管理する必要等も考えると 独自に型定義ファイルを作成するのが良さそうだ。

私は以下を参考に作成した。
https://qiita.com/mtgto/items/e30d1529ca298e49557e

具体的には、
- types/cssprop/index.d.tsを作成
- 上述のimport {} from 'styled-components/cssprop'を追記
- tsconfigに独自作成の型定義ファイルの場所を指定
を実施した。具体的なコードは以下の通り。

これでひとまずビルドは通るようになる。

diff --git a/src/types/cssprop/index.d.ts b/src/types/cssprop/index.d.ts
new file mode 100644
index 0000000..02edd53
--- /dev/null
+++ b/src/types/cssprop/index.d.ts
@@ -0,0 +1 @@
+import {} from 'styled-components/cssprop'
diff --git a/tsconfig.json b/tsconfig.json
index f2850b7..e335802 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -17,7 +17,12 @@
     "resolveJsonModule": true,
     "isolatedModules": true,
     "noEmit": true,
-    "jsx": "react"
+    "jsx": "react",
+    "baseUrl": "./",
+    "typeRoots": [
+      "types",
+      "node_modules/@types"
+    ]
   },
   "include": [
     "src"

スクリーンショット 2020-03-09 20.00.03.png

利用する'styled'関数の変更

ひとまずbuildは通るようになった。が、"border-bottom"が適用されていない。つまりcss propで設定したスタイルはまだ適用されていない。

        <Test css="border-bottom: solid 5px white"> TEST!!!!!! </Test>

ここで再度公式ドキュメントに戻ると以下の記載が確認できる。

You can use the Babel macro to make this work in create-react-app. Unfortunately, Babel macros only run when imported so the import can not be added automatically. The above code works perfectly if you add the import to the macro manually:

// 公式ドキュメントより抜粋
import styled from 'styled-components/macro'

<div
  css={`
    background: papayawhip;
    color: ${props => props.theme.colors.text};
  `}
/>
<Button
  css="padding: 0.5em 1em;"
/>

どうも create-react-app環境では、Babel macroを利用することが出来、その際はimport styled from 'styled-components/macro'を用いる必要があるようだ。この記載にしたがってimportを修正する。

diff --git a/src/App.tsx b/src/App.tsx
index f057b5c..a813609 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,7 +1,7 @@
 import React from 'react';
 import logo from './logo.svg';
 import './App.css';
-import styled from 'styled-components'
+import styled from 'styled-components/macro'

 const Test = styled.div`
   color: red;

これで期待通りにcss propによるスタイルが適用されるようになった。
スクリーンショット 2020-03-09 19.53.06.png

さいごに

この件は半日で解消はしたものの、不要に苦しんだという印象が強い。本記事で同じ苦しみを味わう人が少しでも減ればうれしい。

13
18
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
18