Lit 公式ドキュメントまとめ 3
Reactive properties
Litはリアクティブなクラス変数もしくはプロパティをもちます。 リアクティブというのは、この変数もしくはプロパティに変更があったとき、コンポーネントをアップデートするトリガーとなる特殊な変数になります。
class MyElement extends LitElement { static properties = { name: {}, }; }
このようにstatic properties
で宣言したあとのname
がReactive propertyになります。
主なReactive propertyの役割は以下の4つです
Reactive updates
: LitはReactive propertyごとに内部的にgetter/setterのペアを作ります。このReactive propertyが変更されたときコンポーネントをアップデートします。Attribute handling
: Litはデフォルトで、プロパティに対応する属性をもちます。そしてその属性が変更されたとき、対応しているプロパティも変更されます。オプションでプロパティ値の変更→属性値の変更ということも可能です。Superclass properties
: Litは自動的にスーパクラスで宣言された処理が適用されます。ですので自分の作ったLitのオブジェクト内で処理を上書きしたい場合をのぞいて、Litのアップデートに関わる処理を記載する必要はありません。Element upgrate
: DOMの要素の更新を自動で行ってくれます。
Public propeties and internal state
PublicなReactive propertyは外からその変数を変更することができ、InternalなReactive propertyは自身のコンポーネントからのみ変更することができます。
どちらも変更されるとコンポーネントアップデートのトリガーとなります。
PublicとInternalでReactive propertyを宣言する方法
class MyElement extends LitElement { static properties = { name: {},// Public _age:{state: true},// Internal }; } customElements.define('my-element', MyElement);
<my-element .name="TARO"></my-element> //OK <my-element ._age=20></my-element> //NG
privateなReactive propertyにするには{state: true}とします。こうすることで外側からこのプロパティを変更できなくなります。
クラスフィールド(メンバ変数)を使ってはいけない
JavaScriptではLitのReactive propertyを使う場合はメンバ変数を使ってはいけない。
これは、JavaScriptの仕様上、メンバ変数がLitのReactive propertyより優先度が高くなってしまうため、うまく動かかなくなってしまう可能性があります。
その代わりにコンストラクタで変数を宣言することは可能です。
constructor() { super(); this.data = {}; }
ただしTypeScriptでは以下のルールを守ればメンバ変数を宣言することが可能です。
- tsconfigに
useDefineForClassFields
をfalseで設定すること declare
をフィールドに追加して、フィールド初期化子で初期値を与えること
Babelを使う場合は設定が公式に書いてあるので参照してみてください。
Property options
attribute
LitのReactive propertyはattibuteと連携してます。連携はfalse
でオフにすることができます。オフにした場合はconverter
やreflect
、type
といったオプションが無視されてしまいます。
converter
Reactivr propertyとattributeのカスタムコンバータを定義することができます。
hasChanged
Reactive propertyに値がセットされるたびに自動でこの関数が呼び出されて、前の値から更新されているチェックします。値が更新されている場合はLitのアップデートのトリガーを発火します。
NoAccessor
true
にすることでデフォルトのアクセッサーを生成しません。普通はdefaultのfalse
で運用されます。
reflect
Reactive propertyの値の変更をAttributeにも伝えたい場合、true
にします。デフォルトはfalse
です。通常はAttributeの変更はReactive propertyに伝播しますが、逆は行われません。
State
true
でReactive propertyをinternal
に、false
でpublic
にすることができます。internal
にした場合、外からの変更を受け付けなくなります。
type
Reactive propertyの型を指定することができます。
Reactive Propertyが変更されたとき何が起こるか
Reactive propertyが変更されると、Litのライフサイクルの更新のトリガーが発火し、コンポーネントのテンプレートを再描画します。
このときに何が内部で行われいるのかを説明します。
- プロパティのsetterが呼び出される
- setterがコンポーネントの
requestUpdate
を呼び出す - そのプロパティの前と今の値を比較する
- プロパティが変更されていた場合、非同期でアップデートがスケジュールされます。もしすでにスケジュールされている場合は、スケジュールされるのは1度だけです
- コンポーネントの
update
が呼び出されます。そしてプロパティの変更がattribute
に反映され、コンポーネントのテンプレートが再レンダリングされます。
注意:
もしReactive propertyのarrayの要素を変更した場合、updateのトリガーにはなりません。
なぜならオブジェクト自体が変更されたわけではないので変更とみなされないからです。
Objectやarrayのプロパティの変更について
上記で述べたように、Objectやarrayの変更を検知することができません。
しかしながら、これらを扱うために2つの方法があります。
Immutable data pattern
arrayの要素を更新するつときに、まるごと上書きしてしまう方法です
this.myArray = this.myArray.filter((_, i) => i !== indexToRove)
Manually triggering an update
トリガーであるrequestUpdate()
を直接呼び出す方法です
this.myArray.splice(indexToRemove, 1); this.requestUpdate();
このように任意のタイミングでアップデートのトリガーを発火することができます。
ただし、requestUpdate()
は自身のコンポーネントのみに有効です。
一般的な用途としてはImmutable data pattern
が適しています。
Manually triggering an update
は上級者向けのテクニックとして覚えていてください。