とろろこんぶろぐ

かけだしR&Dフロントエンジニアの小言

styled-componentsでpropsが空オブジェクトでも問題ないのはなぜか?

styled-componentsでpropsから値を指定する

styled-components でよくこういう書き方をすることがある。

const StyledInput = styled.input`
  padding: 20px;
  width: ${({width}) => width};
`;

以下のように props から width の値を伝搬して値を指定することができる。

<StyledInput width="200px">

あえて言うまでもないが、実際にあたるcssは以下のようになる。

.kXcunH {
  padding: 20px;
  width: 200px;
}

styled-componentsでpropsの値を指定しないと?

では、以下のようにwidthを指定しなかったらどうなるだろう?

<StyledInput>

正解を言ってしまうと以下になる。

.kXcunH {
  padding: 20px;
}

つまりわざわざ以下のような書き方をしなくていい。

const StyledInput = styled.input`
  padding: 20px;
  width: ${({width}) => width ? width : ""};
`;

JavaScriptで undefined は文字列になる

ところで以下をjsで実行するとどうなるかというと、

`width: ${(({width}) => width)({})};`

実は以下のように undefined が文字列になってしまう。

"width: undefined;"

styled-components の仕組み

styled-components はなぜ以下のように undefined になってしまわないのか。

.kXcunH {
  padding: 20px;
  width: undefined;
}

その理由は styled-components の style の持ち方と生成方法にある。

そもそも styled-components が styled.div`` という書き方をしているが、 JavaScript 的にどのように展開されるのか。

const p = function(){console.log(arguments)}

p`
  width: ${(({width}) => width)};
`

Array と fucntion が返ってくる。

0: ["↵  width: ", ";↵", raw: Array(2)]
1: ({width}) => width

styled-components ではこれを利用し、 宣言されている const Styled = styled.div`` を保持しておき、

宣言されているComponentのコンテキストでfunction実行しながら、 string配列を1つのstring文字列に展開することで最終的な1つの style を生成して HTML に書き出す。

この時、function実行結果が undefined, null などの falsey values だったら書き出さないようにすることで、

.kXcunH {
  padding: 20px;
  width: ;
}

こういった形式の style が吐き出される。 width: ; はbrowser側で解釈されずに結果として無視されるので、 最終的に以下のようなstyleとして適用される。

.kXcunH {
  padding: 20px;
}

まとめ

styled-components でよくある書き方ではあるものの、 どうしてこれが動くんだろう、とふと思ったので調べた。

const StyledInput = styled.input`
  padding: 20px;
  width: ${({width}) => width};
`;

最近は emotion も流行っているけどどうなんだろう。