フィールド、オブジェクト、配列のリアクティビティ

フィールドの値が変更され、そのフィールドがテンプレートで使用されているか、テンプレートで使用されているプロパティの getter で使用されている場合、コンポーネントは再表示され、新しい値を表示します。フィールドにオブジェクトまたは配列が割り当てられている場合、フレームワークによりオブジェクトまたは配列内部の変更 (新しい値を割り当てた場合など) が監視されます。

コンポーネントが再表示されると、テンプレートで使用されている式が再評価され、renderedCallback() ライフサイクルフックが実行されます。

姓または名の項目に値を入力すると、コンポーネントは名前を大文字に変換して表示します。

名と姓の入力項目。入力された名前は大文字に変換されます。

1<!-- helloExpressions.html -->
2
3<template>
4  <lightning-card title="HelloExpressions" icon-name="custom:custom14">
5    <div class="slds-m-around_medium">
6      <lightning-input
7        name="firstName"
8        label="First Name"
9        onchange={handleChange}
10      ></lightning-input>
11      <lightning-input
12        name="lastName"
13        label="Last Name"
14        onchange={handleChange}
15      ></lightning-input>
16      <p class="slds-m-top_medium">Uppercased Full Name: {uppercasedFullName}</p>
17    </div>
18  </lightning-card>
19</template>

コンポーネントのクラスで firstName 項目と lastName 項目を定義しています。これらは、テンプレートで使用されているプロパティ (uppercasedFullName) の getter で使用されているため、値が変更されるとコンポーネントが再表示されます。

1// helloExpressions.js
2
3import { LightningElement } from "lwc";
4
5export default class TrackExample extends LightningElement {
6  firstName = "";
7  lastName = "";
8
9  handleChange(event) {
10    const field = event.target.name;
11    if (field === "firstName") {
12      this.firstName = event.target.value;
13    } else if (field === "lastName") {
14      this.lastName = event.target.value;
15    }
16  }
17
18  get uppercasedFullName() {
19    return `${this.firstName} ${this.lastName}`.trim().toUpperCase();
20  }
21}

フィールドはリアクティブです。実行時にオブジェクトに追加されるプロパティである Expando プロパティは、リアクティブではありません。

Note

lwc-recipes リポジトリの helloExpressions コンポーネントを参照してください。

Tip

リアクティビティに関する考慮事項 

項目はリアクティブですが、LWC エンジンでは項目値の変更を浅い方法で追跡します。新しい値が項目に追加されると、=== を使用して値の ID が比較され、変更が検出されます。このアプローチは、数値や Boolean などのプリミティブ型に適しています。

1import { LightningElement } from "lwc";
2
3export default class ReactivityExample extends LightningElement {
4  bool = true;
5  number = 42;
6  obj = { name: "John" };
7
8  checkMutation() {
9    this.bool = false; // Mutation detected
10
11    this.number = 42; // No mutation detected: previous value is equal to the newly assigned value
12    this.number = 43; // Mutation detected
13
14    this.obj.name = "Bob"; // No mutation detect: `obj` field value is not reassigned
15    this.obj = { name: "John" }; // Mutation detected - redefining the object with the same value creates a new object
16    this.obj = { ...this.obj, title: "CEO" }; // Mutation detected
17  }
18}

オブジェクトや配列などの複雑なデータ型を操作する場合、変更を検出するには新しいオブジェクトを作成してフィールドに割り当てる必要があります。

複雑なオブジェクトを操作するときにこのような問題を回避するには、@track デコレータを使用して項目値に対する変異を深く追跡します。

オブジェクトおよび配列内の変更の追跡 

オブジェクトのプロパティまたは配列の要素の変更を監視するには、項目を @track でデコレートします。

項目が @track でデコレートされている場合、Lightning Web コンポーネントでは次の内部値に対する変更を追跡します。

  • {} で作成されたプレーンオブジェクト
  • [] で作成された配列

フレームワークでは、ネストされたオブジェクト、ネストされた配列、オブジェクトと配列の混合を含め、プレーンオブジェクトおよび配列に対して行われた変異を再帰的な方法で監視します。循環参照も処理されます。

ただし、フレームワークでは Object から継承するオブジェクト、クラスインスタンス、DateSetMap などの複雑なオブジェクトに対する変異を監視しません。

@track デコレータ

オブジェクトのプロパティの監視 

オブジェクトのプロパティの変更を監視するようにフレームワークに指示するには、項目を @track でデコレートします。

上記のとおり、@track を使用しない場合、フレームワークは、フィールドに新しい値を割り当てる変更を監視します。新しい値と以前の値が === ではない場合、コンポーネントは再表示されます。

Note

たとえば、コードを少し変更して、firstNamelastName の 2 つのプロパティを持つオブジェクトが含まれる fullName フィールドを宣言してみましょう。フレームワークは、fullName に新しい値を割り当てる変更を監視します。

1fullName = { firstName: "", lastName: "" };

このコードは、fullName 項目に新しい値を割り当てるため、コンポーネントが再表示されます。

1// Component rerenders.
2this.fullName = { firstName: "John", lastName: "Doe" };

ただし、オブジェクトのプロパティの 1 つに新しい値を割り当てた場合、コンポーネントは再表示されません。プロパティは監視されていないためです。

1// Component doesn't rerender.
2this.fullName.firstName = "John";

フレームワークは、fullName 項目に新しい値を割り当てる変更を監視します。このコードではこれを行いません。代わりに、fullName オブジェクトの firstName プロパティに新しい値を割り当てます。

オブジェクトのプロパティの変更を監視するようにフレームワークに指示するには、fullName 項目を @track でデコレートします。これで、どちらかのプロパティを変更した場合、コンポーネントは再表示されます。

1// Component rerenders.
2@track fullName = { firstName : '', lastName : ''};
3this.fullName.firstName = 'John';

プロパティにオブジェクトが含まれる場合、オブジェクトのプロパティに対する変更を追跡するには、プロパティに @track のアノテーションを付けます。理解しやすくするため、例を少し変更してみましょう。この例では firstNamelastName をプリミティブ値である空白の文字列に初期化し、これらが変更されたときにコンポーネントが再表示されるようにしています。

新しいプロパティを使用したオブジェクトの再表示 

オブジェクトに @track のアノテーションが付加されているとしても、コンポーネントは前の表示サイクル中にアクセスしたプロパティが更新されている場合のみ再表示されます。これにより、コンポーネントが過度に再表示されるのを防ぎます。

次の追跡されているオブジェクトと、オブジェクトのプロパティを出力する getter を検討します。

1@track obj = {value1: 'Hello'};
2
3get words() {
4    return Object.entries(this.obj)
5              .map(([key, value]) => ({key, value}));
6  }

最初の表示サイクル中に、フレームワークでは obj.value1 がアクセスされたことを記録します。obj に対する変異のうち、value1 に影響しないものはすべて無視されます。これは、表示されるコンテンツに影響しないためです。そのため、value1 に対する変更を行うと再表示がトリガされますが、obj に新しいプロパティを追加するか value2 に対する変更を行っても再表示はトリガされません。

1// Component rerenders.
2setValue1(e) {
3  this.obj.value1 = 'Hello World';
4}
5
6// Component doesn’t rerender.
7setValue2(e) {
8  this.obj.value2 = 'Hello LWC';
9}

新しいプロパティを追加するときにコンポーネントを再表示するには、両方の値を使用してオブジェクトを新しいオブジェクトに割り当てます。

1setValue2(e) {
2  this.obj = {
3    ...this.obj,
4      value2: 'Hello LWC'
5  };
6}

配列の要素の監視 

@track のもう 1 つの使用事例は、配列の要素の変更を監視するようフレームワークに指示することです。

@track を使用しない場合、フレームワークは、フィールドに新しい値を割り当てる変更を監視します。

1arr = ["a", "b"];

arr に新しい値を割り当てると、コンポーネントは再表示されます。

1// Component rerenders.
2this.arr = ["x", "y", "z"];

ただし、配列の要素を更新または追加した場合は、コンポーネントは再表示されません。

1// Component doesn’t rerender.
2this.arr[0] = "x";
3this.arr.push("c");

配列の要素の変更を監視するようにフレームワークに指示するには、arr 項目を @track でデコレートします。さらに、配列の要素の更新についてフレームワークでは自動的に配列を文字列に変換しません。更新された文字列を返すには、getter を使用し、join() を使用して配列の要素を文字列に変換します。

1@track arr = ['a','b'];
2
3get computedArray() { return this.arr.join(','); }
4
5update() {
6  this.arr[0] = 'x';
7  this.arr.push('c');
8}

複雑なオブジェクトの監視 

Date 型のフィールド x を持つコンポーネントを見てみましょう。テンプレートには、x の内部状態を変更するボタンがいくつかあります。次の例で強調しているのは、new Date() でオブジェクトが作成されることです。これはプレーン JavaScript オブジェクトではないため、コードで @track を使用していても、内部状態の変異は LWC エンジンによって監視されません。

1// trackDate.js
2import { LightningElement, track } from "lwc";
3export default class TrackDate extends LightningElement {
4  @track x = new Date();
5
6  initDate() {
7    this.x = new Date();
8  }
9
10  updateDate() {
11    this.x.setHours(7); // No mutation detected
12  }
13}

上の例と同様に、テンプレートには、x の内部状態を変更するボタンがいくつかあります。

1<!-- trackDate.html -->
2<template>
3  <p>Date: {x}</p>
4
5  <button onclick={initDate}>Init</button>
6  <button onclick={updateDate}>Update</button>
7</template>

[Init] ボタンをクリックすると、変更が検出されて、テンプレートが再表示されます。Lightning Web コンポーネントは、x が新しい Date オブジェクトを指していることを追跡しています。一方、[Update] をクリックしても、テンプレートは再表示されません。Lightning Web コンポーネントは Date オブジェクトの値への変更を追跡していないためです。

値が変更されたときにテンプレートが再表示されるようにするには、既存の日付をコピーしてその値を更新します。

1updateDate() {
2  const cloned = new Date(this.x.getTime());
3  cloned.setHours(7);
4
5  // Assign the new date instance to rerender the component.
6  this.x = cloned;
7}

プロパティを追跡できない値に設定すると、警告がログに記録されます。変更に対してコンポーネントが再表示されない状況をデバッグする場合は、ブラウザコンソールを確認してください。たとえば、ブラウザコンソールには、次のような役に立つ警告が出力されています。

Property "x" of [object:vm TrackDate] is set to a non-trackable object, which means changes into that object cannot be observed.

Note

関連トピック

The Japanese Summer '24 guide is now live

日本語の Summer '24 ガイドが公開されました! 「Component Reference (コンポーネントリファレンス)」は、以前と同様にコンポーネントライブラリにあります。