Closed19

[キャッチアップ] styled-components

shingo.sasakishingo.sasaki

今更感も拭えないけど、新しくジョインしたプロジェクトで styled-components を使わざるを得ないのでキャッチアップしていく。食わず嫌いなだけでやってみれば面白いかもしれないしね。

https://styled-components.com/

shingo.sasakishingo.sasaki

styled-components is the result of wondering how we could enhance CSS for styling React component systems.

まず React 特化のライブラリなんだな。それすらよく知らなかった。スタイルが適用された React コンポーネントを作るライブラリでいいのかな。

shingo.sasakishingo.sasaki

どうやら定義したスタイルが必ずコンポーネントと結びついて、そのコンポーネントに対応したクラス名が動的に作られるから、バッティングは絶対しないし、不要コンポーネントは追跡可能だから削除も容易みたいなのがウリらしい。

shingo.sasakishingo.sasaki

ハローワールド代わりに簡単なサンプル書いた

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 上でちゃんとテンプレートリテラル内でコード補完が聴く
  • スタイルが自動で反映されたコンポーネントが作成される
shingo.sasakishingo.sasaki

実態としてはランダムに生成されたクラス名にスタイルが定義されて、このクラスがコンポーネント(h1 や div) に紐づくようになってる。

.hlghNG {
    font-size: 1.5em;
    text-align: center;
    color: red;
}
shingo.sasakishingo.sasaki

このスタイルはコンポーネントが読み込まれたタイミングで動的に 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
shingo.sasakishingo.sasaki

https://nocode-nobug.com/things-to-know-about-styled-components/#how-styles-land-in-the-dom

  • StyledComponents は2つの Class を作成する
    1. sc-[hash] の形式で、コンポーネントを特定するためにユニークキーで、親コンポーネントをセレクタにする際に親コンポーネントを特定するのに使用する
    2. [hash] の形式で、自信のスタイルを直接バインドする
  • クラス名はコンパイルごとに再利用されるためキャッシュが機能しやすい
  • スタイルの付与方法は環境によって異なる
    • development
      • <style> タグを <head> を注入する
      • スタイルタグの中に、コンパイルされたすべてのスタイル定義を追記していく
    • production
      • <style> タグ自体は注入されるが、中身は空になっている
      • スタイル自体はランタイムで JavaScript によって適用される
shingo.sasakishingo.sasaki

コンポーネントは props を受け取って分岐させることも可能

const MyPageHeading = styled.h1<{ center?: boolean }>`
  font-size: 1.5em;
  text-align: ${(props) => props.center && 'center'};
  color: red;
`

一通りちゃんと補完されて嬉しい。

shingo.sasakishingo.sasaki

既存コンポーネントからの拡張も可能

const LargeMyPageHeading = styled(MyPageHeading)`
  font-size: 3em;
`
shingo.sasakishingo.sasaki

as プロパティで使用するタグを差し替える

export const MyComponent: React.FC = () => (
  <Wrapper>
    <MyPageHeading as="a">Hello, World</MyPageHeading>
  </Wrapper>
)
shingo.sasakishingo.sasaki

任意のカスタムコンポーネントでも、 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 />
shingo.sasakishingo.sasaki

StyledComponent は対象のタグまたはカスタムコンポーネントに対する props をそのまま受け流してくれる。

感覚的にはあくまで対象のタグまたはカスタムコンポーネントにスタイル定義を拡張したコンポーネントを作ってくれるって感じなのね。

shingo.sasakishingo.sasaki

StyledComponent はレンダー関数の外側で定義する。中にしちゃうとレンダリングのたびに作成されてあまりにパフォーマンスが悪いとのこと。それはそう。

const Wrapper = ({ message }) => {
  // WARNING: THIS IS VERY VERY BAD AND SLOW, DO NOT DO THIS!!!
  const StyledWrapper = styled.div`
    /* ... */
  `;


  return <StyledWrapper>{message}</StyledWrapper>;
};
shingo.sasakishingo.sasaki

属性付きの要素も使用できる

const Input = styled.input.attrs({ type: "checkbox" })``;

属性は props から動的に設定もできる

const Thing = styled.div.attrs(props) => ({ tabIndex: props.tabIndex }))``
shingo.sasakishingo.sasaki

疑似要素使える

const StyledMyComponent = styled(MyComponent)`
  font-size: 1.5rem;
  &:hover {
    color: red;
  }
`
shingo.sasakishingo.sasaki

ネストも組み合わせられる

const StyledMyComponent = styled(MyComponent)`
  div {
    p {
      color: red;
      &:first-child {
        color: green;
      }
    }
  }
  p {
    color: blue;
  }
`
このスクラップは2023/09/11にクローズされました