[キャッチアップ] styled-components
今更感も拭えないけど、新しくジョインしたプロジェクトで styled-components を使わざるを得ないのでキャッチアップしていく。食わず嫌いなだけでやってみれば面白いかもしれないしね。
公式ドキュメントも Basics だけ軽く読んで世界観掴んでからあとは適当にググったり試したりしよ
styled-components is the result of wondering how we could enhance CSS for styling React component systems.
まず React 特化のライブラリなんだな。それすらよく知らなかった。スタイルが適用された React コンポーネントを作るライブラリでいいのかな。
どうやら定義したスタイルが必ずコンポーネントと結びついて、そのコンポーネントに対応したクラス名が動的に作られるから、バッティングは絶対しないし、不要コンポーネントは追跡可能だから削除も容易みたいなのがウリらしい。
ハローワールド代わりに簡単なサンプル書いた
const MyPageHeading = styled.h1`
font-size: 1.5em;
text-align: center;
color: red;
`
const Wrapper = styled.div`
padding: 4em;
background: papayawhip;
`
export const MyComponent: React.FC = () => (
<Wrapper>
<MyPageHeading>Hello, World</MyPageHeading>
</Wrapper>
)
- タグ付きテンプレートリテラルでスタイルをベタ書きする
- 何の機能かしらないけど、vscode 上でちゃんとテンプレートリテラル内でコード補完が聴く
- スタイルが自動で反映されたコンポーネントが作成される
実態としてはランダムに生成されたクラス名にスタイルが定義されて、このクラスがコンポーネント(h1 や div) に紐づくようになってる。
.hlghNG {
font-size: 1.5em;
text-align: center;
color: red;
}
このスタイルはコンポーネントが読み込まれたタイミングで動的に styled-components 用の style タグにスタイルが追加されるみたい。
<style data-styled="active" data-styled-version="5.3.11">
.hlghNG {
font-size: 1.5em;
text-align: center;
color: red;
}
</style>
一方でコンポーネントがアンマウントされても挿入された style タグが削除されるということは無さそうで、色んなコンポーネントを読むほどにどんどん追記される感じだ。
document.querySelector('style[data-styled=active]').textContent.length
1416
document.querySelector('style[data-styled=active]').textContent.length
14709
document.querySelector('style[data-styled=active]').textContent.length
17658
document.querySelector('style[data-styled=active]').textContent.length
18466
document.querySelector('style[data-styled=active]').textContent.length
27506
と思ったが、↑は開発モードの場合の話で、プロダクションだともっと最適化されるっぽい
- StyledComponents は2つの Class を作成する
-
sc-[hash]
の形式で、コンポーネントを特定するためにユニークキーで、親コンポーネントをセレクタにする際に親コンポーネントを特定するのに使用する -
[hash]
の形式で、自信のスタイルを直接バインドする
-
- クラス名はコンパイルごとに再利用されるためキャッシュが機能しやすい
- スタイルの付与方法は環境によって異なる
- development
-
<style>
タグを<head>
を注入する - スタイルタグの中に、コンパイルされたすべてのスタイル定義を追記していく
-
- production
-
<style>
タグ自体は注入されるが、中身は空になっている - スタイル自体はランタイムで JavaScript によって適用される
-
- development
コンポーネントは props を受け取って分岐させることも可能
const MyPageHeading = styled.h1<{ center?: boolean }>`
font-size: 1.5em;
text-align: ${(props) => props.center && 'center'};
color: red;
`
一通りちゃんと補完されて嬉しい。
既存コンポーネントからの拡張も可能
const LargeMyPageHeading = styled(MyPageHeading)`
font-size: 3em;
`
as
プロパティで使用するタグを差し替える
export const MyComponent: React.FC = () => (
<Wrapper>
<MyPageHeading as="a">Hello, World</MyPageHeading>
</Wrapper>
)
任意のカスタムコンポーネントでも、 className を受け取るようにすれば StyledComponent 経由でスタイルを当てることができる。
const MyComponent: React.FC<{ className?: string }> = (props) => <div className={props.className}>MyComponent</div>
const StyledMyComponent = styled(MyComponent)`
font-size: 1.5rem;
color: red;
`
export const NewDocGroupModal: React.FC<Props> = () => <StyledMyComponent />
StyledComponent は対象のタグまたはカスタムコンポーネントに対する props をそのまま受け流してくれる。
感覚的にはあくまで対象のタグまたはカスタムコンポーネントにスタイル定義を拡張したコンポーネントを作ってくれるって感じなのね。
StyledComponent はレンダー関数の外側で定義する。中にしちゃうとレンダリングのたびに作成されてあまりにパフォーマンスが悪いとのこと。それはそう。
const Wrapper = ({ message }) => {
// WARNING: THIS IS VERY VERY BAD AND SLOW, DO NOT DO THIS!!!
const StyledWrapper = styled.div`
/* ... */
`;
return <StyledWrapper>{message}</StyledWrapper>;
};
属性付きの要素も使用できる
const Input = styled.input.attrs({ type: "checkbox" })``;
属性は props から動的に設定もできる
const Thing = styled.div.attrs(props) => ({ tabIndex: props.tabIndex }))``
疑似要素使える
const StyledMyComponent = styled(MyComponent)`
font-size: 1.5rem;
&:hover {
color: red;
}
`
ネストも組み合わせられる
const StyledMyComponent = styled(MyComponent)`
div {
p {
color: red;
&:first-child {
color: green;
}
}
}
p {
color: blue;
}
`
だいたいわかって一旦業務で使えそうなのでヨシ