3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ReactでCSS変数をstyle属性に定義して子孫コンポーネントから参照する

Posted at

はじめに

Reactで親コンポーネントの動的な値(幅や高さなど)を、深くネストした子コンポーネントに伝えたいことがあります。

propsで渡すのが基本ですが、CSS変数をstyle属性に定義することで、propsを使わずに子孫要素から値を参照できます。

基本的な使い方

親コンポーネントでCSS変数を定義

import { CSSProperties } from 'react';

const Parent = () => {
  const [width, setWidth] = useState(400);

  return (
    <div
      style={{
        '--container-width': `${width}px`,
        width: 'var(--container-width)',
      } as CSSProperties}
    >
      <Child />
    </div>
  );
};

ポイント:

  • CSS変数は--で始まる名前をつける
  • TypeScriptではas CSSPropertiesでキャストが必要

子コンポーネントから参照

// propsで幅を受け取る必要がない
const Child = () => {
  return (
    // 親で定義したCSS変数を参照
    <div className="w-[calc(var(--container-width)-24px)]">
      <p className="truncate">長いテキスト...</p>
    </div>
  );
};

CSSのvar()関数でCSS変数を参照できます。Tailwind CSSのarbitrary valuesでも使えます。

何が嬉しいのか

propsのバケツリレーが不要

通常、深くネストした子コンポーネントに値を渡すには、中間のコンポーネントすべてにpropsを経由させる必要があります。

<Parent width={width}>
  <Section width={width}>
    <Container width={width}>
      <Card width={width} />  {/* やっと使う */}
    </Container>
  </Section>
</Parent>

CSS変数なら、親で定義するだけで子孫のどこからでも直接参照できます。

<Parent style={{ '--width': `${width}px` }}>
  <Section>
    <Container>
      <Card />  {/* var(--width) で参照 */}
    </Container>
  </Section>
</Parent>

動的な値でも機能する

stateで管理している値をCSS変数に入れれば、値が変わるたびにCSS変数も更新されます。リサイズ可能なパネルの幅など、頻繁に変わる値にも対応できます。

子コンポーネントの再レンダーを抑制できる

propsで幅を渡すと、値が変わるたびに子コンポーネントも再レンダーされます。

CSS変数を使えば、子コンポーネントのpropsは変わらないため、React.memoと組み合わせることで子の再レンダーをスキップできます。

// memo化した子コンポーネント
const Card = memo(() => {
  return (
    <div className="w-[var(--width)]">...</div>
  );
});

レイアウトの更新はブラウザのCSS再計算で行われ、Reactの再レンダーは親コンポーネントだけで済みます。

デメリット

依存関係が暗黙的になる

propsなら「このコンポーネントはwidthを必要とする」と型で明示されますが、CSS変数は暗黙の依存です。

// props: 依存が明確
const Card = ({ width }: { width: number }) => { ... }

// CSS変数: 親に --width が定義されている前提(型で表現できない)
const Card = () => {
  return <div className="w-[var(--width)]">...</div>
}

どのCSS変数に依存しているかはコードを読まないとわかりません。

デバッグしづらい

型安全ではない

CSS変数名はただの文字列なので、typoしても型エラーになりません。

まとめ

使い所はよく考えよう!

3
1
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
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?