にわかプラス

にわかが玄人になることを夢見るサイトです。社会や国際のトレンド、プログラミングや電子工作のことについて勉強していきたいです。

Lit公式ドキュメントまとめ Reactive property

sponsor

Lit 公式ドキュメントまとめ 3

www.niwaka-plus.com

www.niwaka-plus.com

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でオフにすることができます。オフにした場合はconverterreflecttypeといったオプションが無視されてしまいます。

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に、falsepublicにすることができます。internalにした場合、外からの変更を受け付けなくなります。

type

Reactive propertyの型を指定することができます。

Reactive Propertyが変更されたとき何が起こるか

Reactive propertyが変更されると、Litのライフサイクルの更新のトリガーが発火し、コンポーネントのテンプレートを再描画します。
このときに何が内部で行われいるのかを説明します。

  1. プロパティのsetterが呼び出される
  2. setterがコンポーネントのrequestUpdateを呼び出す
  3. そのプロパティの前と今の値を比較する
  4. プロパティが変更されていた場合、非同期でアップデートがスケジュールされます。もしすでにスケジュールされている場合は、スケジュールされるのは1度だけです
  5. コンポーネントの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は上級者向けのテクニックとして覚えていてください。

www.niwaka-plus.com

www.niwaka-plus.com