画面フローのリアクティビティに関するベストプラクティス (ベータ)

Lightning Web コンポーネントがフローランタイムエンジンに適切に統合され、画面フローの反応が期待通りに動作することを確認します。

状態管理とリアクティビティ 

以下のセクションでは、画面フローでリアクティビティを使用した次のような状態管理について説明します。

  • LWC と Aura の状態管理の違い
  • 新しい LWC フローランタイムでの状態管理の仕組み
  • 画面フロー用の LWC を作成する際に従うべき状態管理とイベントのベストプラクティス

Aura と LWC での状態管理 

Aura コンポーネントと LWC の状態管理は、異なる原則に従っています。Aura コンポーネントでは、<aura:attributes /> は、それらが属するコンポーネント (外部コンポーネントを含む) によって表示および変更されます。LWC では内部と外部の状態を明確に分離するように設計されています。@api で宣言された変数は、LWC の親によってのみ変更されることが意図されています。@api アノテーションのない変数と @track アノテーションのある変数は内部状態とみなされ、これらを定義している LWC 内で変更できます。

画面フローでの LWC の状態管理 

フローランタイムは、LWC の状態管理の設計原則に従ったものです。これは、表示される LWC コンポーネントが自身の @api 属性を変更しない場合に最適に機能します。代わりに、コンポーネントは FlowAttributeChangeEvent を起動することによって変更を要求します。これはいくつかの理由から不可欠です。

  • 親が @api プロパティを制御できるコンポーネントは、コンポーネント自身の内部ビジネスロジックではなく、アプリケーションから状態を制御できるため、より簡単に再利用できます。
  • 属性変更イベントを起動することで、フローランタイムはコンポーネントの状態を正確に把握できます。フローランタイムは、リアクティビティの必須要素である条件項目の可視性など、コンポーネント間のインタラクションを制御できます。

LWC リンタルールの使用は、コンポーネントが @api プロパティを変更しないように指定できる優れた方法です。開発環境で LWC のリンタルールを使用するリンタを設定し、特にこの設定が強制されるように指定します。

フローが遷移するときの状態管理 

フローランタイムのバージョン間で一貫した動作を維持するため、ユーザが [次へ] ボタンまたは [完了] ボタンをクリックすると、フローランタイムは、次の画面に移動するかフローを終了する前に、各カスタム LWC コンポーネントの @api 属性の現在の状態を抽出して値を収集します。このメカニズムにより、規定されている設計パターンに準拠していないコンポーネントが、最新のフローランタイムバージョンでも引き続き動作します。この動作には依存しないことを強く推奨します。

次に、LWC の @api プロパティを更新する代わりに FlowAttributeChangeEvent を実行する、単純なテキスト入力コンポーネントの例を示します。

1@api textValue;
2
3handleTextInputChange(event) {
4  const event = new FlowAttributeChangeEvent('textValue', event.target.value);
5  this.dispatchEvent(event);
6}

次の例は非推奨です。

1//Don’t do this
2@api textValue;
3
4handleTextInputChange(event) {
5  this.textValue = event.target.value;
6}

FlowAttributeChangeEvent インスタンスの作成 

FlowAttributeChangeEvent イベントにより、LWC では現在の状態をランタイムに通知できます。これは、フローランタイムの状態管理メカニズムに含まれる機能です。このイベントは、ユーザとのインタラクションで、@api プロパティの更新を必要とする変更が行われるたびに起動するように意図されています。

lightning/flowSupport モジュールから FlowAttributeChangeEvent をインポートします。

1import { FlowAttributeChangeEvent } from "lightning/flowSupport";

次に、イベントのインスタンスを作成できます。

1const attributeChangeEvent = new FlowAttributeChangeEvent(apiParameterName, updatedValue);

FlowAttributeChangeEvent には 2 つの引数が含まれています。

  • apiParameterName — 変更する API プロパティの名前。
  • updatedValue — フロー属性の更新された値。

JavaScript および .js-meta.xml ファイルに表示される API プロパティと一致する文字列を apiParameterName に渡します。

たとえば、exampleApiParameterName が次の 3 か所のすべてでどのように一貫しているかを確認してください。

  • LWC の .js-meta.xml ファイルで属性を宣言します。

    1<property name="exampleApiParameterName" label="Example Api Parameter" .../>
  • LWC の .js ファイルで API プロパティを宣言します。

    1@api exampleApiParameterName;
  • FlowAttributeChangeEvent インスタンスを作成します。

    1const changeEvent = new FlowAttributeChangeEvent("exampleApiParameterName", "newValue");

.js-meta.xml ファイル内で宣言されたデータ型と一致するように updatedValue を渡します。

  • プロパティをレコードとして宣言します。

    1const changeEvent = new FlowAttributeChangeEvent("exampleApiParameterName", {
    2  StringFieldName: "stringValue",
    3  BooleanFieldName: false,
    4  NumberFieldName: 42,
    5  DateTimeFieldName: "2022-12-15T23:35:24.000Z",
    6});
  • プロパティを文字列として宣言します。

    1const changeEvent = new FlowAttributeChangeEvent("exampleApiParameterName", "newValue");
  • プロパティを数値として宣言します。

    1const changeEvent = new FlowAttributeChangeEvent("exampleApiParameterName", 6);
  • プロパティを Boolean として宣言します。

    1const changeEvent = new FlowAttributeChangeEvent("exampleApiParameterName", true);

FlowAttributeChangeEvent イベントの起動 

FlowAttributeChangeEvent インスタンスを作成したら、this.dispatchEvent(eventToFire) メソッドを使用してイベントを起動できます。

1const attributeChangeEvent = new FlowAttributeChangeEvent("apiPropertyName", 10);
2this.dispatchEvent(attributeChangeEvent);

FlowAttributeChangeEvent のベストプラクティス 

これらの FlowAttributeChangeEvent の例に従って、問題のある使用方法を避け、フローバージョンのアップグレード後に予期しない動作が発生する可能性を低減してください。次のベストプラクティスにより、コンポーネントをより簡単に管理できるようになります。

  • イベントの起動は、イベントハンドラ内、またはイベントハンドラ内から呼び出されるメソッド内で実行します。
  • イベントの値パラメータを次のデータ型に制限します: 文字列、数値、Boolean 値、JSON (レコードタイプの場合)。
  • イベントの値パラメータの Flow データ型を LWC の @api プロパティのデータ型と一致させます。
  • 必要に応じて get/set パターンを使用して、@api プロパティの変更に反応するように設定します。

次の例では、getter と setter を使用して @api プロパティの更新を数える方法と、属性の変更イベントを起動する前にカウンタの値を増やす方法を示します。どちらのパターンも有効ですが、得られる結果は若干異なります。

1@api textValue;
2textValueToRender;
3changeCounter = 0;
4
5get textValue() {
6  return this.textValueToRender;
7}
8
9// Due to the nature of the Flow Runtime, this setter method
10// is called after the FlowAttributeChangeEvent fired below. It also
11// is invoked when reactivity leads to a change to the textValue property.
12set textValue(newTextValue) {
13  this.changeCounter++;
14  this.textValueToRender = newTextValue;
15}
16
17handleTextInputChange(event) {
18  const event = new FlowAttributeChangeEvent('textValue', event.target.value);
19  this.dispatchEvent(event);
20}

この場合、ユーザがテキスト入力に変更を入力するたび、および textValue プロパティが変更されたとフローランタイムで判定されるたびに、this.changeCounter が増分されます。この方法では、コンポーネントの初期化と、リアクティビティによるコンポーネント間のインタラクションが考慮されます。中間変数を使用して、set メソッドからの更新を表示します。この例では、textValueToRender がこの役割を果たします。

次のコード例では、属性変更イベントを起動する前にカウンタを増分します。このシナリオでは、get/set パターンを使用しません。

1@api textValue;
2changeCounter = 0;
3
4handleTextInputChange(event) {
5  this.changeCounter++;
6  const event = new FlowAttributeChangeEvent('textValue', event.target.value);
7  this.dispatchEvent(event);
8}

この場合、ユーザがテキスト入力に変更を入力するたびに this.changeCounter が増分されます。リアクティビティによってこの項目の値が変化しても、カウンタは更新されません。

アノテーションのないプロパティまたは @track プロパティを使用して、変更可能なローカルコンポーネントの状態を管理します。

次の例は、ユーザに入力テキストボックスといくつかの色見本を表示するカラーピッカーを示しています。カラーピッカーコンポーネントはフローに color を返すので、color にのみ @api アノテーションが必要です。selectedSwatchIdinputValue メンバーは内部のみで、デコレータは必要ありません。

1//colorPicker.js
2
3@api color;
4
5inputValue;
6selectedSwatchId;
7
8
9ALL_SWATCHES =[{
10  'id': 1,
11  'color': 'red'
12}, {
13  'id': 2,
14  'color': 'blue'
15}, {
16  'id': 3,
17  'color': 'green'
18}, {
19  'id': 4,
20  'color': 'yellow'
21}];
22
23set color(newColor) {
24  // Set the content of the input text to the color value:
25  this.inputValue = newColor;
26
27  // If the color matches a swatch, highlight it in the UI:
28  const foundSwatch = this.ALL_SWATCHES.find(swatch => swatch.color === newColor);
29  this.selectedSwatchId = foundSwatch && foundSwatch.id;
30}
31
32get color() {
33  return this.inputValue;
34}
35
36handleSwatchSelected(event) {
37  // Select get the swatch data for the clicked swatch:
38  const selectedSwatch = this.ALL_SWATCHES.find(swatch => swatch.id === event.target.dataset.id);
39  const changeEvent = new FlowAttributeChangeEvent('color', selectedSwatch.color);
40  this.dispatchEvent(changeEvent);
41}
42
43handleInputValueChange(event) {
44  this.inputValue = event.target.value;
45  const changeEvent = new FlowAttributeChangeEvent('color', this.inputValue);
46  this.dispatchEvent(changeEvent);
47}

get メソッドを使用して複数の @api プロパティを結合し、ビューの派生変数を作成します。

1//Salutation.js
2@api firstName;
3@api lastName;
4@api ageName;
5
6get salutation() {
7  return `Hello ${this.firstName} ${this.lastName}, you are ${this.age} years old.`;
8}
1<!-- Salutation.html -->
2<template>
3  <div>{salutation}</div>
4</template>

前の例よりも複雑なシナリオなどで、すべての @api プロパティの set メソッドに、内部コンポーネントの状態管理に必要な派生メンバー変数に寄与する同じロジックを適用します。このロジックによって、どの寄与属性に加えられた変更も、設定された属性の順序に関係なく、値が正しくレンダリングされます。

1//Dropdown.js
2set optionLabels(newOptionLabels) {
3  this.internalOptionLabels = newOptionLabels;
4  this.options = this.computeOptions();
5}
6
7set optionValues(newOptionValues) {
8  this.internalOptionValues = newOptionValues;
9  this.options = this.computeOptions();
10}
11
12set value(newValue) {
13  this.internalValue = newValue;
14  this.options = this.computeOptions();
15}
16
17handleSelect(event) {
18  const selectedValue = event.srcElement.value;
19  const isAlreadySelected = this.options.some(option => {
20    return option.isSelected && option.value === selectedValue;
21  });
22
23  if (!isAlreadySelected) {
24    const attributeChange = new FlowAttributeChangeEvent('value', selectedValue);
25    this.dispatchEvent(attributeChange);
26  }
27}
28
29computeOptions() {
30  if (this.internalOptionLabels.length !== this.internalOptionValues.length) {
31    return [];
32  }
33
34  return this.internalOptionLabels.map((label, index) ==> {
35    const value = this.internalOptionValues[index];
36    const isSelected = this.internalValue === value;
37    return {
38      label,
39      value,
40      isSelected
41    };
42  });
43}

@api プロパティは変更しないでください。代わりに、FlowAttributeChangeEvents を起動します。

作成後に FlowAttributeChangeEvent パラメータを変更しないでください。デフォルトで FlowAttributeChangeEvent インスタンスは composed と bubble が有効になっています。

1const changeEvent = new FlowAttributeChangeEvent("prop", "value");
2changeEvent.bubbles = false; // Don’t do this
3changeEvent.composed = false; // Don’t do this

競合状態が発生する可能性があるため、FlowAttributeChangeEventsFlowNavigationXxx イベントを同時に起動しないでください。ナビゲーションプロセスが開始されるまでに、Lightning Web コンポーネントが更新された値を表示する時間が確保されるわけではありません。

ナビゲーション変更イベントのベストプラクティス 

FlowAttributeChangeEvent に加えて、Lightning Web コンポーネントは、画面間の移動、フローの一時停止または終了のために、ナビゲーションイベントを起動できます。ナビゲーションイベントを使用するには、次の関数を lightning/flowSupport からインポートします。

  • FlowNavigationNextEvent
  • FlowNavigationBackEvent
  • FlowNavigationPauseEvent
  • FlowNavigationFinishEvent

FlowNavigationEvent の起動 

次のスニペットは、ナビゲーションイベントをインポートして起動する方法を示しています。この例では FlowNavigationNextEvent を使用していますが、使用可能なナビゲーション対象のそれぞれにも同じ構造が当てはまります。

1import { FlowNavigationNextEvent } from "lightning/flowSupport";
2
3export default class FlowExample extends LightningElement {
4  handleCustomNavigationButtonClick() {
5    const nextEvent = new FlowNavigationNextEvent();
6    this.dispatchEvent(nextEvent);
7  }
8}

ナビゲーションイベントを使用すると、Lightning Web コンポーネントに追加の制御メカニズムを含めることができます。エンドユーザの操作に反応するときにナビゲーションイベントが起動されます。ナビゲーションイベントを使用する場合は、次のガイドラインを参照してください。

  • イベントハンドラ (またはイベントハンドラが呼び出すメソッド) でナビゲーションイベントを起動します。
  • ユーザエクスペリエンスが低下するため、イベントハンドラの外部でナビゲーションイベントを起動しないでください。
  • renderedCallbackconnectedCallback などのライフサイクルハンドラでナビゲーションイベントを起動しないでください。
  • 画面をスキップするロジックは、画面のライフサイクルではなく、フロー決定ノードに配置します。
  • ダミー画面でナビゲーションを実行しないでください。フローのパフォーマンスが低下する可能性があります。
  • フロー決定ノードでデータがまだ利用できないために、画面の初期化フェーズ内でナビゲーションを実行する必要があるシナリオが発生した場合は、Idea Exchange にアップグレードの要求を投稿してください。

The Japanese Summer '24 guide is now live

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