1. LWC デコレータとは?
LWC のデコレータとは、親子関係にあるコンポーネント間でのイベント伝達、値の受け渡し、リレンダー、Apex処理の呼び出しを行う。
代表的なデコレータは以下の 3 つ
-
@api
: 親コンポーネントとのインターフェース -
@track
: オブジェクトや配列のリアクティブ管理 -
@wire
: レコードへのアクセスやサーバーサイド(Apex)とのデータ連携
2. @api
🔹 特徴
-
@api
は、親 → 子の一方向通信を実現するための「公開インターフェース」- 「外部(親)からアクセスしてOKです」という公開マーク
→外部に公開する側(= 子)にだけ必要
- 「外部(親)からアクセスしてOKです」という公開マーク
- 一方向通信(親 → 子)専用
✅ @api の基本ルール
-
@api
は 子コンポーネントにだけ必要。- 親側には書かない。
@api
がついていないプロパティやメソッドは、親からアクセスできない(隠蔽される)。
- 親側には書かない。
- 子 → 親へ値を戻したいときは カスタムイベント (
dispatchEvent
) を使う。
🔹 親 → 子 → 親 へ値を戻したい場合
-
@api
は親→子の一方向通信なので、逆方向には使えない - **子 → 親に値を戻したい場合は、カスタムイベント(dispatchEvent)を使用する!
⚙️ どういうときに使う?
- 親コンポーネントから 値を渡す、メソッドを呼ぶ の2つの用途で使われる。
使いみち | 例 |
---|---|
親 → 子にデータを渡す | 親で入力された値を子に表示させる |
親 → 子のメソッドを呼ぶ | 親から子の初期化・リセット・開閉制御など |
🔹 例:親 → 子で値を渡す
ChildComponent.js
import { LightningElement, api } from 'lwc';
export default class ChildComponent extends LightningElement {
@api message;
}
ParentComponent.html
<template>
<c-child-component message="Hello Child!"></c-child-component>
</template>
🔹 例:親 → 子のメソッドを呼ぶ
ChildComponent.js
import { LightningElement, api } from 'lwc';
export default class ChildComponent extends LightningElement {
@api
sayHello() {
console.log('Hello from Child!');
}
}
ParentComponent.js
import { LightningElement } from 'lwc';
export default class ParentComponent extends LightningElement {
handleClick() {
this.template.querySelector('c-child-component').sayHello();
}
}
ParentComponent.html
<template>
<lightning-button label="Call Child" onclick={handleClick}></lightning-button>
<c-child-component></c-child-component>
</template>
3. @track
🔹 特徴
@track
はオブジェクトや配列の内部の変更をリアクティブに反映するためのデコレータ- LWC はプリミティブ型(文字列・数値・真偽値など)は自動でリアクティブ化されるので、
@track
は不要。 - 逆にオブジェクトや配列などの中身を変えるだけの場合は
@track
が必要。
✅ どういう時に使えばええの?
状況 | 必要? |
---|---|
プリミティブ型(文字列・数値・true/false) | ❌ 不要 |
オブジェクト・配列の中身だけを変える | ✅ 必要 |
オブジェクト・配列を丸ごと新しく作り直して代入する | ❌ 不要 |
✅ 必要になるケース
🔹 例)オブジェクトの中身だけを変えたい
import { LightningElement, track } from 'lwc';
export default class ExampleTrack extends LightningElement {
@track person = { name: 'Taro', age: 30 };
changeName() {
this.person.name = 'Jiro'; // @track がないと画面に反映されない!
}
}
🔹 例)配列に要素を追加する
import { LightningElement, track } from 'lwc';
export default class ExampleTrackArray extends LightningElement {
@track fruits = ['Apple'];
addFruit() {
this.fruits.push('Orange'); // @track がないと画面に反映されない!
}
}}
✅ 不要になるケース
🔹 オブジェクト or 配列を丸ごと置き換える
import { LightningElement } from 'lwc';
export default class ExampleNoTrack extends LightningElement {
person = { name: 'Taro' };
changeName() {
this.person = { ...this.person, name: 'Jiro' }; // 新しいオブジェクトで置き換え → OK!
}
}
or
import { LightningElement } from 'lwc';
export default class ExampleNoTrackArray extends LightningElement {
fruits = ['Apple'];
addFruit() {
this.fruits = [...this.fruits, 'Orange']; // 新しい配列を代入 → OK!
}
}
🔹プリミティブ型
import { LightningElement } from 'lwc';
export default class ExamplePrimitive extends LightningElement {
name = 'Taro';
changeName() {
this.name = 'Jiro'; // 文字列だけなので @track は不要!
}
}
⚠️ @track
の注意点
- 最近の LWC では
@track
を使用するよりも「オブジェクトや配列を丸ごと新しいものに置き換える」書き方が主流らしい? - 理由 = バグりにくい / 保守しやすい
🔑 ポイント
中身だけ変える →
@track
丸ごと代入できる →@track
いらない
👉 迷ったら 「新しく作り直して代入する」 で OK!
4. @wire
🔹 特徴
- LWC コンポーネントと Apex(または LDS)をつなぐ自動配線
-
@wire
は**「データ取得だけ」ではなく「データの自動更新」** にも対応できる。
✅ 使い方3パターン
使い方 | おすすめシーン |
---|---|
@wire プロパティ形式 |
データを取って画面に出すだけ |
@wire 関数形式 |
ローディング表示や追加処理を入れたい |
Imperative Apex 呼び出し | ボタン押下などユーザー操作で任意タイミング |
✅ まずは Apex 側の準備
必ず@AuraEnabled(cacheable=true)
を付ける。
public with sharing class ContactController {
@AuraEnabled(cacheable=true)
public static List<Contact> getContacts() {
return [SELECT Id, Name, Email FROM Contact LIMIT 10];
}
}
🔹 例1: @wire
プロパティ形式
import { LightningElement, wire } from 'lwc';
import getContacts from '@salesforce/apex/ContactController.getContacts';
export default class ContactList extends LightningElement {
@wire(getContacts) contacts;
}
<template>
<template if:true={contacts.data}>
<template for:each={contacts.data} for:item="contact">
<p key={contact.Id}>{contact.Name}</p>
</template>
</template>
<template if:true={contacts.error}>
<p>エラー: {contacts.error.message}</p>
</template>
</template>
🔑 ポイント
• contacts は { data, error } を自動で持つ
• 成功すれば contacts.data、失敗すれば contacts.error
• 加工できないので、表示だけの用途に最適
🔹 例2: @wire
関数形式
import { LightningElement, wire } from 'lwc';
import getContacts from '@salesforce/apex/ContactController.getContacts';
export default class ContactListFunction extends LightningElement {
contacts;
error;
isLoading = true;
@wire(getContacts)
wiredContacts({ data, error }) {
this.isLoading = false;
if (data) {
this.contacts = data;
this.error = undefined;
} else if (error) {
this.error = error;
this.contacts = undefined;
}
}
}
<template>
<template if:true={isLoading}>
<p>Loading...</p>
</template>
<template if:true={contacts}>
<template for:each={contacts} for:item="contact">
<p key={contact.Id}>{contact.Name}</p>
</template>
</template>
<template if:true={error}>
<p>エラー: {error.message}</p>
</template>
</template>
🔑 ポイント
• wiredContacts({ data, error }) の形で結果を分解
🔹 例3: Imperative Apex (明示的呼び出し)
import { LightningElement } from 'lwc';
import getContacts from '@salesforce/apex/ContactController.getContacts';
export default class ImperativeExample extends LightningElement {
contacts;
error;
handleClick() {
getContacts()
.then(result => {
this.contacts = result;
this.error = undefined;
})
.catch(error => {
this.error = error;
this.contacts = undefined;
});
}
}
<template>
<lightning-button label="取引先責任者を取得" onclick={handleClick}></lightning-button>
<template if:true={contacts}>
<template for:each={contacts} for:item="contact">
<p key={contact.Id}>{contact.Name}</p>
</template>
</template>
<template if:true={error}>
<p>エラー: {error.message}</p>
</template>
</template>
🔑 ポイント
• getContacts() を then で受け取る
• catch でエラーをハンドリング
• 任意のタイミングで呼べるのが強み
✅ そもそも @AuraEnabled
とは?
• Apex メソッドを LWC や Aura コンポーネントから呼び出せるようにする ためのアノテーション。(これがないと JavaScript から呼べない。)
✅ cacheable=true の意味
• @wire は 非同期通信でデータを取るときにキャッシュ可能(= データの再利用が可能) なメソッドじゃないと使えない。
• @wire は リアクティブな自動更新 を担うので、Salesforce の LDS(Lightning Data Service)と同じく、読取専用 & キャッシュ前提で動く。
✅ なぜ必須なのか
• @wire は 「読取専用」 だから、取得して終わりなので、データ更新系(INSERT, UPDATE, DELETE)は使えない。→ cacheable=true を指定して、「これは読取専用だよ!キャッシュして良いよ!」 と明示する必要がある。