solid js の Solid Tagged Template Literals のメモ
Solid Tagged Template Literals についてのメモ
signal 呼び出しの際の記法
document が↓と説明するように、${...}
に入れるものには注意が必要。
Reactive expression must be manually wrapped in functions to be reactive.
const [count, setCount] = createSignal(0)
return html`
<div>${count}</div> <!-- 🆗: 変更は追跡される -->
<div>${count()}</div> <!-- ❌: 変更は追跡されない -->
<div>${() => count}</div> <!-- 🆗: 変更は追跡される -->
<div>${() => count()}</div> <!-- 🆗: 変更は追跡される -->
`
制御フローコンポーネントに signal を渡すとき
const [bol, setBool] = createSignal(false)
return html`
<${Show} when=${bol}>...</Show> <!-- 🆗: 変更は追跡される -->
<${Show} when=${bol()}>...</Show> <!-- ❌: 変更は追跡されない -->
<${Show} when=${() => bol}>...</Show> <!-- ❌: 変更は追跡されない -->
<${Show} when=${() => bol()}>...</Show> <!-- 🆗: 変更は追跡される -->
`
ただし、「signal を使った条件式を用いる」が「派生signal は定義したくない」場合は、when = ${() => bol() + piyo}
の記法を使うしかない。
カスタムのコンポーネントに signal を渡すとき
- コンポーネント内部の呼び出し記法が
<p>${props.count}</p>
のときreturn html` <${Counter} count=${count}> <!-- ❌: 変更は追跡されない --> <${Counter} count=${count()}> <!-- ❌: 変更は追跡されない --> <${Counter} count=${() => count}> <!-- 🆗: 変更は追跡される--> <${Counter} count=${() => count()}> <!-- ❌: 変更は追跡されない --> `
- コンポーネント内部の呼び出し記法が
<p>${props.count()}</p>
のときreturn html` <${Counter} count=${count}> <!-- 🔥: エラーになる --> <${Counter} count=${count()}> <!-- 🔥: エラーになる --> <${Counter} count=${() => count}> <!-- ❌: 変更は追跡されない--> <${Counter} count=${() => count()}> <!-- 🔥: エラーになる --> `
- コンポーネント内部の呼び出し記法が
<p>${() => props.count}</p>
のときreturn html` <${Counter} count=${count}> <!-- 🆗: 変更は追跡される --> <${Counter} count=${count()}> <!-- ❌: 変更は追跡されない --> <${Counter} count=${() => count}> <!-- 🆗: 変更は追跡される--> <${Counter} count=${() => count()}> <!-- 🆗: 変更は追跡される --> `
- コンポーネント内部の呼び出し記法が
<p>${() => props.count()}</p>
のときreturn html` <${Counter} count=${count}> <!-- 🔥: エラーになる --> <${Counter} count=${count()}> <!-- 🔥: エラーになる --> <${Counter} count=${() => count}> <!-- 🆗: 変更は追跡される --> <${Counter} count=${() => count()}> <!-- 🔥: エラーになる --> `
まとめ:template literal で signal を用いるための記法
成功することがない『カスタムの Comp. に渡して${props.count()}
で使う』場合を省くと、以下のようになる。
${count} |
${count()} |
${() => count} |
${() => count()} |
|
---|---|---|---|---|
<p>{...}</p> などの通常時 |
🆗 | ❌ | 🆗 | 🆗 |
制御フロー Comp. に渡す | 🆗 | ❌ | ❌ | 🆗 |
カスタムの Comp. に渡して${props.count} で使う |
❌ | ❌ | 🆗 | ❌ |
カスタムの Comp. に渡して${() => props.count} で使う |
🆗 | ❌ | 🆗 | 🆗 |
カスタムの Comp. に渡して${() => props.count()} で使う |
🔥 | 🔥 | 🆗 | 🔥 |
カスタムのコンポーネントの中で signal を使う
上述のように、カスタムのコンポーネントに signal を渡すときは ${() => count}
が最も安全。
この記法で signal を渡した状態で、以下の2つを比較した。
-
props.count
として直接html
で使う記法 (上の実験とほぼ同じ内容) -
props.count
を利用した新しい変数を作ってそれをhtml
で使う記法
それぞれの記法の結果
新しく作成した変数を使う場合、それがprops.count
を利用した無名関数、つまり派生 signal でなければうまく機能しない。
- ❌:
const is_even = (props.count() %2 == 0) ? true : false
- 🆗:
const is_even = () => (props.count() %2 == 0) ? true : false
まとめ:signal は基本的に「関数の中」で呼び出す
- signal を普通に使うとき
html`<span> ${ () => `count: ${count()}` } </span>` // 無名関数でラップその1
html`<span> count: ${ () => count() } </span>` // 無名関数でラップその2
const text = () => `count: ${ count() }` // 派生 signal としてラップ
html`<span>${ () => text() }</span>`
- その他 🆗:
<span>${count}</span>
や<span>${text}</span>
[1] - その他 ❌:
<span>${count()}</span>
や<span>${text()}</span>
- signal を制御フローの判定で使うとき
<${Show} when=${() => count() %2 == 0}> ... </Show>
- 派生 signal を使う場合 🆗:
<${Show} when=${is_even}> ... </Show>
- 派生 signal を使う場合 ❌:
<${Show} when=${is_even()}> ... </Show>
- signal をカスタムコンポーネントに props として渡すとき
// 渡すとき
html`<${MyComp} count=${() => count}><//>`
// コンポーネントの中で使うとき (signal を普通に使うときと同じ)
html`<span> ${ () => `count: ${props.count()}` } </span>` // 無名関数でラップその1
html`<span> count: ${ () => props.count() } </span>` // 無名関数でラップその2
const text = () => `count: ${ props.count() }` // 派生 signal としてラップ
html`<span>${ () => text() }</span>`
その他の性質も「1. signal を普通に使うとき」と同様
-
が、上の実験でわかるように、この記法でうまく動くかどうかは props の渡し方や新しい変数の定義方法に依存する ↩︎
Tabler Icons を使う
hashrock さんの方法は solidjs ではうまくいかないので、solidjs 用に調整された?ものを使う
使い方の説明が雑すぎてライブラリとアクセスパスの対応が分かりづらいが、ここから推測できる。
というわけで Tabler Incons は /tb
であり、以下のようにして利用できる。stroke width の設定は solidJS の style 設定機能を使う[1][2]。
import { render } from "https://cdn.skypack.dev/solid-js/web"
import html from "https://cdn.skypack.dev/solid-js/html"
import { TbPlaylist } from "https://esm.sh/solid-icons@1.0.1/tb"
const App = () => {
return html`
<div>
<${TbPlaylist} size='36' style=${ {"stroke-width": 1} }><//>
</div>
`}
render(App, document.body)
コンポーネントからの ref の転送
[1]。
template literal の callback を要求する性質によって動作がわかりやすいconst App = () => {
let some_elem
return html`
<div>
<${MyComp} ref=${(el) => some_elem= el}><//>
</div>
`}
render(App, document.body)
export function MyComp( props ){
return html`
<div>なにか1</div>
<div>なにか2</div>
<div ref=${(el) => props.ref(el)} >ref に格納したい element </div>
`
}
-
と思ったがそうでもない?
some_elem = el
と(el) => some_elem=el
は等しくはないよな... まあ自身を取得する処理関連で ref は when とか同様の特殊処理になっている気はする。 ↩︎
use どうやって使うか問題
公式のシンタックスシュガー云々を基準にすると、(el) => ref = el
ではなく (el) => func(el)
しているのでは?
${}
の挙動とその利用
タグ付き template literal 内部での基本的に「関数を変数名で指定して渡す」 or 「完全に固定されたデータを渡す」の2択
-
${function}
なら関数が渡されてその関数が html に結び付けられる -
${function()}
なら関数の template 評価時の返り値が渡されてその値が html に結び付けられる - なので、 signal getter を
${function()}
で記述してしまうと signal の変更が追跡されない
この性質の結果、template literal 内部の signal 関連の記述は、全て派生 signal を要求する。
したがって、${() => function()+1}
のような『一時的な無名の派生 signal を作成して渡す』記述になる。
- が、ref や when や onClick のような named な部分は記法は同じだが処理が違うっぽい?