lit-element で関数渡すときの this が面倒
- カテゴリ:
- JavaScript
- コメント数:
- Comments: 0
◆ コンポーネント内でリスナ登録なら bind 不要
◆ 子コンポーネントに渡すと bind 必要になる
◆ どこで呼び出すか気にせずすべてアロー関数で書いて bind 済みにしてしまうほうが良さそう
◆ 子コンポーネントに渡すと bind 必要になる
◆ どこで呼び出すか気にせずすべてアロー関数で書いて bind 済みにしてしまうほうが良さそう
久々に lit-element を使ったら this に悩まされました
CustomElement はクラス構文なので this が出てくるのが嫌なところなんですよね
この点は React の関数コンポーネントのほうが好きです
問題点ですが こういう HTML を用意しました
main-elem の内側に sub-elem があります
main-elem では doSomething 関数を定義して button のクリックリスナに設定と sub-elem に渡しています
sub-elem 側では単純に受け取った関数を sub-elem 内の button のクリックリスナに設定しています
main-elem と sub-elem のそれぞれの button のクリックで main-elem の doSomething 関数が実行されます
このときの this は何か です
まず main-elem の button をクリックしたときですが lit-element がリスナを登録して呼び出すときにはそのクラスのインスタンスのコンテキストで呼び出してくれます
なので this は main-elem で this.foo は main となっています
これは嬉しい機能です
問題は sub-elem のほうで 関数をプロパティとしてセットするときには特に何もしてくれません
bind されていないので sub-elem 側でクラスのインスタンスのコンテキストで呼び出されます
その結果 this は sub-elem になってしまいます
this.foo は sub となっています
リスナ登録で this を bind してくれるのなら 関数をプロパティに渡すところでもやってほしいのですけどね
ただ それだとプロパティとして渡す関数がメソッドなのか判断できないので コンポーネント外の関数を渡す場合もすべて bind されることになります
それで困ることはあまりなさそうですが 動作的には変な感じがしますし テンプレートの記述は lit-html 側の機能なので自動でインスタンスに bind するのも難しそうです
とは言っても 自分で bind するのも面倒です
特に 自分のコンポーネント内でリスナ登録するときは bind いらないのに 子コンポーネントへ渡す必要ができたら bind が必要になるというのも扱いづらく嫌な部分です
子コンポーネントに渡す必要ができたときに bind 漏れが多発する気しかしません
変に便利機能でコンポーネント内なら bind 不要というのがあるので面倒に思いますが それは気にせずに render みたいなものを除くすべてのメソッドはアロー関数形式で書いたほうがいいのかもしれないです
CustomElement はクラス構文なので this が出てくるのが嫌なところなんですよね
この点は React の関数コンポーネントのほうが好きです
問題点ですが こういう HTML を用意しました
<!doctype html>
<script type="module">
import { LitElement, html, css } from "https://unpkg.com/lit-element@2.4.0/lit-element.js?module"
customElements.define("main-elem", class extends LitElement {
foo = "main"
doSomething() {
console.log(this.foo)
}
render() {
return html`
<button @click=${this.doSomething}>main</button>
<sub-elem .fn=${this.doSomething}></sub-elem>
`
}
})
customElements.define("sub-elem", class extends LitElement {
foo = "sub"
static properties = {
fn: { type: Function },
}
render() {
return html`
<button @click=${this.fn}>sub</button>
`
}
})
</script>
<main-elem></main-elem>
main-elem の内側に sub-elem があります
main-elem では doSomething 関数を定義して button のクリックリスナに設定と sub-elem に渡しています
sub-elem 側では単純に受け取った関数を sub-elem 内の button のクリックリスナに設定しています
main-elem と sub-elem のそれぞれの button のクリックで main-elem の doSomething 関数が実行されます
このときの this は何か です
まず main-elem の button をクリックしたときですが lit-element がリスナを登録して呼び出すときにはそのクラスのインスタンスのコンテキストで呼び出してくれます
なので this は main-elem で this.foo は main となっています
これは嬉しい機能です
問題は sub-elem のほうで 関数をプロパティとしてセットするときには特に何もしてくれません
bind されていないので sub-elem 側でクラスのインスタンスのコンテキストで呼び出されます
その結果 this は sub-elem になってしまいます
this.foo は sub となっています
リスナ登録で this を bind してくれるのなら 関数をプロパティに渡すところでもやってほしいのですけどね
ただ それだとプロパティとして渡す関数がメソッドなのか判断できないので コンポーネント外の関数を渡す場合もすべて bind されることになります
それで困ることはあまりなさそうですが 動作的には変な感じがしますし テンプレートの記述は lit-html 側の機能なので自動でインスタンスに bind するのも難しそうです
とは言っても 自分で bind するのも面倒です
特に 自分のコンポーネント内でリスナ登録するときは bind いらないのに 子コンポーネントへ渡す必要ができたら bind が必要になるというのも扱いづらく嫌な部分です
子コンポーネントに渡す必要ができたときに bind 漏れが多発する気しかしません
変に便利機能でコンポーネント内なら bind 不要というのがあるので面倒に思いますが それは気にせずに render みたいなものを除くすべてのメソッドはアロー関数形式で書いたほうがいいのかもしれないです
customElements.define("main-elem", class extends LitElement {
foo = "main"
doSomething = () => {
console.log(this.foo)
}
render() {
return html`
<button @click=${this.doSomething}>main</button>
<sub-elem .fn=${this.doSomething}></sub-elem>
`
}
})