この記事の概要
つい先日こちらの記事を投稿しました。
React 19 でstyle
の巻き上げができるようになる前提であれば、もしかして……という程度の実験です。
私はプロジェクトの依存関係が増えるのは、あまり好きではありません。
必要なものは使えば良いと思いますが、不用意に増やしたくないので、たまに「今当たり前に使っているライブラリ、実は剥がせないかな」と考えています。
この記事も似たようなモチベーションで調べた内容です。
実戦投入できるほどの検証はしていませんが、こういう記事をきっかけに「ってことはコレならできるんじゃない?」なんて話が出たら良いなあと思って投稿します。
前回記事のコンポーネントをベースに土台を用意する
まずは前回の記事の内容をベースに、コンポーネントを用意しました。
export function TestComponent({ color, size }) {
return (
<>
<style href={`test-component-${color}`} precedence="medium">{`
.test-component-${color} {
color: ${color};
}
`}</style>
<style href={`test-component-${size}`} precedence="medium">{`
.test-component-${size} {
font-size: ${size}px;
}
`}</style>
<p className={`test-component-${color} test-component-${size}`}>This is a test component</p>
</>
);
};
今のままだと、生成されるクラスはこのようなものです。
- test-component-red
- test-component-blue
- test-component-16
- test-component-24
コンポーネント名をつけているからある程度は大丈夫な気がしつつも、どこで被りがおきるか分かりません。
ここからスコープをつけていきます。
crypto.randomUUID()
を使ってユニークな id を生成する
React にはuseId
という hook があり、ユニークな id を生成することができます。
しかし:
の文字からはじまるので className 属性や id 属性には使いづらい1のと、同じコンポーネントでも生成される度に違う id を付与するという性質がスタイリングにおいてはイマイチです。
コンポーネントの外側でランダムな id を指定できて、かつ依存関係を増やさなくていいやり方はないものか……と探していたところ Web Crypto API に出会いました。
用途が違うように思いますが、ひとまず実験なので良しとします。
まずコンポーネントの外側でランダムな id を生成します。
コンポーネントの内側で生成するとコンポーネントの数だけstyle
要素が生まれてしまい、今回の恩恵に預かれません。
+ const uuid = crypto.randomUUID();
export function TestComponent({ color, size }) {
return (
<>
<style href={`test-component-${color}`} precedence="medium">{`
.test-component-${color} {
color: ${color};
}
`}</style>
<style href={`test-component-${size}`} precedence="medium">{`
.test-component-${size} {
font-size: ${size}px;
}
`}</style>
<p className={`test-component-${color} test-component-${size}`}>This is a test component</p>
</>
);
};
data-*
属性を使ってスコープを作る
crypto.randomUUID()
で生成された id が数字始まりなことは多いです。
すると className 属性や id 属性に割り当てても認識されません。2
しかし、data-*
属性ならそういった縛りはありません。
というわけで新たに作成し、割り当てます。
const uuid = crypto.randomUUID();
export function TestComponent({ color, size }) {
return (
<>
<style href={`test-component-${color}`} precedence="medium">{`
.test-component-${color} {
color: ${color};
}
`}</style>
<style href={`test-component-${size}`} precedence="medium">{`
.test-component-${size} {
font-size: ${size}px;
}
`}</style>
- <p className={`test-component-${color} test-component-${size}`}>This is a test component</p>
+ <p
+ data-component-id={uuid}
+ className={`test-component-${color} test-component-${size}`}
+ >
+ This is a test component
+ </p>
</>
);
};
style
に割り当てる
href
とセレクター、両方に割り当てます。
const uuid = crypto.randomUUID();
export function TestComponent({ color, size }) {
return (
<>
- <style href={`test-component-${color}`} precedence="medium">{`
+ <style href={`${uuid}-${color}`} precedence="medium">{`
- .test-component-${color} {
+ [data-component-id="${uuid}"].test-component-${color} {
color: ${color};
}
`}</style>
- <style href={`test-component-${size}`} precedence="medium">{`
+ <style href={`${uuid}-${size}`} precedence="medium">{`
- .test-component-${size} {
+ [data-component-id="${uuid}"].test-component-${size} {
font-size: ${size}px;
}
`}</style>
<p
data-component-id={uuid}
className={`test-component-${color} test-component-${size}`}
>
This is a test component
</p>
</>
);
};
これらを呼び出してみます。
red
, blue
, 20
を 2 回ずつ使っています。
import { TestComponent } from "./TestComponent";
export default function App() {
return (
<>
<TestComponent color="red" size={10} />
<TestComponent color="red" size={20} />
<TestComponent color="blue" size={20} />
<TestComponent color="blue" size={30} />
</>
);
}
実際に生成されたstyle
のコードはこのようになっていました。
<style data-href="1752b6a4-995b-483e-b439-b9d1d22504b9-red" data-precedence="medium">
[data-component-id="1752b6a4-995b-483e-b439-b9d1d22504b9"].test-component-red {
color: red;
}
</style>
<style data-href="1752b6a4-995b-483e-b439-b9d1d22504b9-10" data-precedence="medium">
[data-component-id="1752b6a4-995b-483e-b439-b9d1d22504b9"].test-component-10 {
font-size: 10px;
}
</style>
<style data-href="1752b6a4-995b-483e-b439-b9d1d22504b9-20" data-precedence="medium">
[data-component-id="1752b6a4-995b-483e-b439-b9d1d22504b9"].test-component-20 {
font-size: 20px;
}
</style>
<style data-href="1752b6a4-995b-483e-b439-b9d1d22504b9-blue" data-precedence="medium">
[data-component-id="1752b6a4-995b-483e-b439-b9d1d22504b9"].test-component-blue {
color: blue;
}
</style>
<style data-href="1752b6a4-995b-483e-b439-b9d1d22504b9-30" data-precedence="medium">
[data-component-id="1752b6a4-995b-483e-b439-b9d1d22504b9"].test-component-30 {
font-size: 30px;
}
</style>
重複なくスタイルを宣言できました。
最後に
一旦形になったとは言え、問題はたくさんあります
- エディタで補完がきかない
-
props
の数が増えるとコードの管理が煩雑 -
style
の数が多い
などなど。
今の時点で実用的とは思いませんが、しばらく経ってから意外と役立つこともあるだろう、という希望的観測とともにこの記事は終わりです。
-
この記事で書いたような処理を通せば使えますが、そこまでするほどか?という疑問が出てきます。 https://qiita.com/xrxoxcxox/items/54bb72a350866fa2d2c7 ↩
-
これもまたエスケープすれば認識されるのですが、それはそれで大変そうだな……と感じて違う方法を探すに至りました。 https://www.w3.org/TR/css-syntax-3/#escaping ↩