はじめに
Salesforceのレコードに紐づく画面をLWCで開発する際、@api recordIdと書いておくとそのrecordIdをプラットフォーム側が解決して、サーバー側に対してレコードに関する情報を要求できます。
レコードページにそのまま置くようなコンポーネントであればよいのですが、クイックアクションになるとちょっと微妙な点が出てきます。
それが、connectedCallback()の時点ではまだrecordIdが解決されない点です。そのため、クイックアクションのconnectedCallback()では、カスタムのApexアクションなどにrecordIdを使うことが難しいです。これを簡単に解決する小テクがあります。
動かない例
@AuraEnabledなApexメソッドでrecordIdからレコードを紹介するパターンです。これはクイックアクション内では動かないです。(connectedCallback()の実行がrecordIdの解決よりも先なので)
import someApexMethod from '@salesforce/apex/SomeClass.someApexMethod';
export default class QuickAction extends LightningElement {
@api recordId;
data;
async connectedCallback() {
this.data = await someApexMethod({ recordId: this.recordId }); // this.recordIdはundefined
}
}
動かす小テク
これを正攻法で解決するには@wireを使うことですが、@wireはキャッシュが強く残ったり不便なことが多いです。そのため、ちょっとした小テクを入れます。
具体的にはasync connectedCallback()の冒頭にawait this.recordId;と書いておきます。(本当はawaitするなら何でもいい)
import someApexMethod from '@salesforce/apex/SomeClass.someApexMethod';
export default class QuickAction extends LightningElement {
@api recordId;
data;
async connectedCallback() {
await this.recordId;
this.data = await someApexMethod({ recordId: this.recordId }); // this.recordIdは解決されている
}
}
解説
これがなぜ動くかを理解するには、以下の2点を理解する必要があります。
- LWCのライフサイクルフックの実行順
- JavaScriptにおけるイベントループ・タスクキュー・非同期処理の仕様
LWCのライフサイクルフックの実行順
大体こんな感じです。
constructorconnectedCallbackrenderedCallback-
wireの結果が評価される
参考: https://salesforce.stackexchange.com/questions/377190/order-of-execution-of-wire-in-lwc
ここで大事なのは以下の2点です。
-
connectedCallback()がwireより先に実行される - クイックアクションのLWCでは、
recordIdがwireのタイミングでクエリパラメータから解決される
JavaScriptの非同期処理
JSのasync関数は初めてのawaitより後の処理をthenハンドラとしてPromiseを返します。そして、そこでコールスタックを抜けるため、awaitより後の処理は後回しにされます。そのため、もともとの実行順が変化します。
constructor-
connectedCallbackのawait以前 renderedCallback-
wireの結果が評価される←recordId解決 -
connectedCallbackのawaitより後の処理←recordIdを使う処理
今回のテクは無駄なawaitを書くことで、プラットフォームによるrecordIdの解決を待ってからconnectedCallbackを実行するようにしています。
awaitするのはなんでもいいのですが、recordIdを待つというのが意味的に分かりやすいのでawait this.recordId;としています。
やってることはsetTimeout(~~~, 0)とかのテクニックとほぼ同じです。