この記事はバージョン Spring ’26 において執筆しています。
現在の動作と異なる場合がありますので、ご認識おきください。
第1回、第2回の連載を通じて、@lwc/stateを利用してUIコンポーネントからビジネスロジックを分離し、fromContextを用いて複数コンポーネント間で安全に状態を共有する手法を学んできました。
しかし、実際のSalesforce開発において、状態管理の対象となるデータの大部分は「Salesforceのレコードデータ」や「オブジェクトのメタデータ」です。従来、LWCでこれらのデータを取得するには、コンポーネントのJavaScriptファイル内で@wireデコレータを使用してLightning Data Service(LDS)を呼び出すのが標準でした。
ここでアーキテクチャ上の大きな壁にぶつかります。@wireアダプターはコンポーネントのライフサイクルと強く結びついているため、UIを持たない純粋なJavaScriptモジュール(状態マネージャー)の内部では使用できないのです。
では、データ取得のロジックはこれまで通りUIコンポーネント側に残し、取得した結果だけをアクション経由で状態マネージャーに渡すべきでしょうか? Salesforceは、この問題をよりエレガントに解決するために「組み込み状態マネージャー(Built-in State Managers)」を提供しています。
本機能はSpring ’26においてベータ版であり、Experience Cloudでの動作は未対応となっています。
今回の記事で作成したコンポーネントの全ソースコードをGitHubで公開しています。ぜひ Clone して、あなたの組織で動かしてみてください。
組み込み状態マネージャー(Built-in State Managers)とは
Salesforceは、プラットフォームのデータとメタデータへアクセスするための標準機能として、以下のような組み込み状態マネージャー(ベータ版)を提供しています。
lightning/stateManagerRecord: 特定のレコードデータ(UI APIのgetRecord相当)を取得・管理する。lightning/stateManagerLayout: オブジェクトのページレイアウト情報やメタデータを取得する。
これらは@wireの代わりとなるものであり、コンポーネントコンテキストに依存せず、独自のカスタム状態マネージャーの内部に「ネスト(入れ子)」して組み込むことができるよう設計されています。
これにより、データの取得から加工、そしてUIへの提供までを、単一のAPIモジュール内に完全にカプセル化することが可能になります。
実践:ネストされた状態マネージャーによるレコード管理
それでは、Lightning Experience環境で動作する「取引先(Account)ビューア」を例に、高度な状態マネージャーを構築してみましょう。
今回は、動的に渡されたレコードIDをもとに取引先データを取得し、必要に応じて別のレコードを読み込み直すことができる状態マネージャーを作成します。
高度な状態管理モジュール (accountStore)
ここでは、カスタムのdefineStateの中で、標準のstateManagerRecordをネストして使用します。
accountStore.js
import { defineState } from '@lwc/state';
// 組み込み状態マネージャーをインポート
import smRecord from 'lightning/stateManagerRecord';
export default defineState(({ atom, computed, setAtom }, initialRecordId) => {
// 1. 組み込みマネージャーの設定情報を保持する「atom」を定義
// この設定自体をリアクティブにすることで、動的な再フェッチが可能になります
const recordConfig = atom({
recordId: initialRecordId,
fields: ['Account.Name', 'Account.Industry', 'Account.Phone']
});
// 2. ネストされた状態マネージャーの初期化
// 設定atomを直接渡すことで、設定が変更されると自動的にデータを再取得します
const recordManager = smRecord(recordConfig);
// 3. UIコンポーネント向けに、使いやすい形にデータを整形(派生状態)
// 組み込みマネージャーは status, data, error というプロパティを持ちます
const isLoading = computed([recordManager], (mgr) => mgr.status === 'loading' || mgr.status === 'unconfigured');
const errorMessage = computed([recordManager], (mgr) => mgr.error? mgr.error.body.message : null);
const accountInfo = computed([recordManager], (mgr) => {
if (mgr.status === 'loaded' && mgr.data) {
return {
name: mgr.data.fields.Name.value,
industry: mgr.data.fields.Industry.value,
phone: mgr.data.fields.Phone.value
};
}
return null;
});
// 4. アクション:別のレコードIDを読み込む
const loadNewAccount = (newRecordId) => {
// 設定atomのrecordIdを更新するだけで、smRecordが自動的に新データをフェッチする
setAtom(recordConfig, {
recordId: newRecordId,
fields: ['Account.Name', 'Account.Industry', 'Account.Phone']
});
};
// 5. コンポーネントに公開するAPI
return {
isLoading,
errorMessage,
accountInfo,
loadNewAccount
};
});
UIコンポーネント (accountViewer)
状態マネージャーがデータ取得に関する複雑な非同期処理(ローディング状態の管理、エラーハンドリング、データの抽出など)をすべて引き受けてくれたため、UIコンポーネントのコードは極限までシンプルになります。
accountViewer.js
import { LightningElement, api } from 'lwc';
import accountStore from 'c/accountStore';
export default class AccountViewer extends LightningElement {
// ページから渡されるレコードID
@api recordId;
// コンポーネントの初期化時に状態マネージャーを生成
// (※このコンポーネントをContextのプロバイダーとして配下の子コンポーネントに共有することも可能です)
state;
connectedCallback() {
// レコードIDを初期値として渡してインスタンス化
this.state = accountStore(this.recordId);
}
handleReloadAnother() {
// サンプル:ボタンが押されたら別のデモ用取引先IDをロードする
const anotherAccountId = '001gK00000TuugWQAR';
this.state.value.loadNewAccount(anotherAccountId);
}
}
accountViewer.html
<template>
<lightning-card title="取引先ビューア(@lwc/state)" icon-name="standard:account">
<div class="slds-p-around_medium">
<template lwc:if={state.value.isLoading}>
<lightning-spinner alternative-text="Loading" size="small"></lightning-spinner>
</template>
<template lwc:elseif={state.value.errorMessage}>
<div class="slds-text-color_error">
データの取得に失敗しました: {state.value.errorMessage}
</div>
</template>
<template lwc:elseif={state.value.accountInfo}>
<dl class="slds-list_horizontal slds-wrap">
<dt class="slds-item_label slds-text-color_weak slds-truncate">取引先名:</dt>
<dd class="slds-item_detail slds-truncate">{state.value.accountInfo.name}</dd>
<dt class="slds-item_label slds-text-color_weak slds-truncate">業種:</dt>
<dd class="slds-item_detail slds-truncate">{state.value.accountInfo.industry}</dd>
<dt class="slds-item_label slds-text-color_weak slds-truncate">電話番号:</dt>
<dd class="slds-item_detail slds-truncate">{state.value.accountInfo.phone}</dd>
</dl>
<div class="slds-m-top_medium">
<lightning-button label="別のレコードをロード" onclick={handleReloadAnother}></lightning-button>
</div>
</template>
</div>
</lightning-card>
</template>
アーキテクチャの解説とメリット
この設計(ネストされた状態マネージャー)がもたらす最大の利点は、「関心の完全な分離(Separation of Concerns)」です。
- データ取得の抽象化
UIコンポーネントは、背後でLDSが動いているのか、UI APIが呼ばれているのかを一切知る必要がありません。ただstatusがloadedになったかどうかを監視し、データを表示するだけです。 - インテリジェントなリアクティビティ
組み込みのsmRecordに渡す設定(recordConfig)自体をatomで定義したことで、UIからアクション(loadNewAccount)を呼び出してatomを更新するだけで、連鎖的に新しいレコードのデータフェッチが走ります。 - 複数データソースの統合
今回はsmRecord一つでしたが、要件によってはsmLayoutを同時に組み込んだり、複数のレコードデータを一つのcomputed内で結合して「アプリケーションに必要な完全なデータモデル」を構築してからUIに渡すことも可能です。
Lightning Experienceでの動作確認(レコードページへの配置)
今回の accountViewer コンポーネントは、現在表示している取引先のレコードID(@api recordId)を初期状態として受け取るように設計されています。そのため、取引先のレコードページに配置して動作を確認します。
- ページへの配置: プロジェクトを組織にデプロイした後、Salesforce上で任意の「取引先」のレコード詳細画面を開き、コンポーネントを配置します。
- 初期ロード時の動作: レコード画面に戻ると、配置したコンポーネントが現在の取引先IDを自動的に取得して状態マネージャーに渡します。状態マネージャー内部の
smRecordがデータを取得し、ローディングスピナーが消えた後に取引先名や業種が画面に表示されます。 - 動的な再フェッチの確認: コンポーネント内の「別のレコードをロード」ボタンをクリックしてみてください。UI側からのアクション呼び出しにより、状態マネージャー内部の
recordConfig(設定用atom)のrecordIdが書き換わります。すると、組み込み状態マネージャーがその変更を自動的に検知して新たなレコードデータへと動的に再フェッチを行い、画面をシームレスに更新します。
このように、UIコンポーネントは単に「初期IDを渡す」「アクションを呼ぶ」だけであり、データフェッチやリアクティビティの複雑な制御はすべて状態マネージャー内に美しくカプセル化されていることが実感できるはずです。


連載のまとめ:LWCアーキテクチャのパラダイムシフト
全3回にわたり、ベータ版として提供されている@lwc/stateの全貌を解説してきました。
- 第1回では、
atomやcomputedを用いて、散らかりがちなコンポーネント内の状態を中央集権化する基礎を学びました。 - 第2回では、
fromContextによるContextパターンを用いて、プロップスドリリング(バケツリレー)を排除し、複数コンポーネントで単一の情報を同期する実践的なウィザードを構築しました。 - 第3回(今回)では、組み込み状態マネージャーをネストさせ、
@wireに依存せずにSalesforceデータと密接に連携するページレベルの堅牢なデータ層を設計しました。
従来のLWC開発では、「表示コンポーネント」が自らデータを取りに行き、自らロジックを計算し、他のコンポーネントにイベントで通知するというように、一つのコンポーネントが多くの責務を抱え込みすぎていました。 @lwc/stateの導入は、こうしたモノリシック(一枚岩)なコンポーネント設計から脱却し、最新のフロントエンド標準に沿ったスケーラブルなアーキテクチャへの進化をSalesforce開発者にもたらします。
複雑なエンタープライズ要件に直面した際は、ぜひこの新しい状態管理のアプローチを取り入れてみてください。
参考URL
Manage State Across LWC Components with State Managers (Beta)





読者の声