組み込みコンポーネント
PrefXには、各コンポーネントの2番目の引数(utils)からアクセスできる4つの組み込みコンポーネントが付属しています。これらは、追加の依存関係なしに一般的なUIパターンを解決します。
| コンポーネント | 目的 | 利用可能性 |
|---|---|---|
For | リアクティブアイテムを含むリストをレンダリングします | utils.For |
Catch | レンダリングエラーをキャッチして処理します | utils.Catch |
Portal | 子要素を別のDOMノードにレンダリングします | utils.Portal |
Defer | 値が準備できるまでプレースホルダーを表示します | utils.Defer |
For
リアクティブリストレンダラー。アイテムのシグナル(またはプレーンな配列)を受け取り、変更されたアイテムのみを再レンダリングします。リストが空の場合に表示するオプションのfallbackをサポートしています。
タイプ署名
const For: PC<{
items: Signal<any[]>;
callback: (item: any) => any;
fallback?: any;
}>プロパティ
| プロパティ | 型 | 必須 | 説明 |
|---|---|---|---|
items | Signal<any[]> | はい | アイテムの配列を保持するシグナル。シグナルが変更されると、For はリストを効率的に調整します。追加されたアイテムはレンダリングされ、削除されたアイテムはクリーンアップされます。 |
callback | (item: any) => any | はい | 各アイテムに対して呼び出される関数。そのアイテムに対してレンダリングする JSX を返します。返された値はキャッシュされ、アイテムの値自体が変更された場合にのみ再計算されます。 |
fallback | any | いいえ | 配列が空の場合にレンダリングされるコンテンツ。 |
使用法
import type { PC } from 'preffx';
type Item = { id: number; name: string };
const App: PC = (_, { signal, For }) => {
const items = signal<Item[]>([
{ id: 1, name: 'Alpha' },
{ id: 2, name: 'Beta' },
{ id: 3, name: 'Gamma' },
]);
return (
<ul>
<For
items={items}
callback={(item: Item) => (
<li key={item.id}>{item.name}</li>
)}
fallback={<li>No items</li>}
/>
</ul>
);
};反応型更新
シグナル内の配列を変更すると、DOMが自動的に更新されます。
// Add an item
items.value = [...items.value, { id: 4, name: 'Delta' }];
// Remove an item
items.value = items.value.filter((i) => i.id !== 2);
// Replace all items
items.value = [];
// -> fallback "No items" is shown配列内の信号要素
配列内の各要素は、それ自体が信号となり得ます。要素の信号値が変化すると、その要素の出力のみが再計算されます。
const items = signal([
signal({ id: 1, name: 'Alpha' }),
signal({ id: 2, name: 'Beta' }),
]);
// Update the second item — only "Beta" re-renders
items.value[1].value = { id: 2, name: 'BETA' };Catch
子コンポーネントによってスロー(または返された)エラーを捕捉し、代わりにフォールバックをレンダリングします。Reactのエラー境界にヒントを得ています。
タイプ署名
const Catch: PC<{
fallback?: any;
children?: any | any[];
}>プロパティ
| プロパティ | 型 | 必須 | 説明 |
|---|---|---|---|
children | any | any[] | いいえ | レンダリングする子要素。子要素が Error インスタンスである場合(または Error インスタンスを返す場合)、エラーが捕捉されます。 |
fallback | any | いいえ | エラーが捕捉されたときに表示するコンテンツ。静的な値、または { errors, ...extraProps } を受け取る 関数コンポーネント を指定できます。 |
静的コンテンツとしてフォールバックする
import type { PC } from 'preffx';
const App: PC = (_, { Catch }) => {
return (
<div>
<Catch fallback={<div>Something went wrong</div>}>
<MaybeBrokenComponent />
</Catch>
</div>
);
};フォールバックを関数コンポーネントとして扱う
フォールバック関数は、errors(捕捉されたエラーの配列)と、<Catch>に渡されるその他のプロパティを受け取ります。
const App: PC = (_, { Catch }) => {
return (
<Catch
fallback={({ errors }) => (
<div class="error-fallback">
<h3>{errors.length} error(s) occurred</h3>
</div>
)}
>
<UnstableComponent />
</Catch>
);
};予備選手に余分な小道具を渡す
const App: PC = (_, { Catch }) => {
return (
<Catch
fallback={({ errors, severity }) => (
<div class={severity}>
Errors: {errors.length}
</div>
)}
severity="critical"
>
<UnstableComponent />
</Catch>
);
};エラーの検出方法
Catch は、flat(Infinity) を使用してすべての子要素を再帰的にスキャンします。instanceof Error を介して Error インスタンスである子要素があれば、フォールバック処理が実行されます。
Portal
子要素をコンポーネントのルート以外のDOMノードにレンダリングします。モーダル、ツールチップ、ドロップダウン、通知などに便利です。
タイプ署名
const Portal: PC<{
root: HTMLElement;
children?: any | any[];
}>プロパティ
| プロパティ | 型 | 必須 | 説明 |
|---|---|---|---|
root | HTMLElement | はい | 子要素をレンダリングする対象のDOM要素。 |
children | any | any[] | いいえ | ポータルルート内にレンダリングするコンテンツ。 |
基本的な使い方
import type { PC } from 'preffx';
const Modal: PC = (_, { Portal }) => {
const portalRoot = document.getElementById('modal-root')!;
return (
<Portal root={portalRoot}>
<div class="modal-overlay">
<div class="modal-content">Hello from portal!</div>
</div>
</Portal>
);
};ポータル内のリアクティブなコンテンツ
ポータル内の子は完全に反応します。信号により自動的に更新されます。
const Notification: PC<{ message: string }> = (
{ message },
{ signal, Portal },
) => {
const count = signal(0);
return (
<div>
<Portal root={document.getElementById('toast-root')!}>
<span id="notification-count">{count}</span>
</Portal>
<button onClick={() => (count.value += 1)}>+</button>
</div>
);
};クリーンアップ
親コンポーネントが破棄されると、Portal は自動的に子コンポーネントをクリーンアップし、root.replaceChildren() によってポータルルートをクリアします。
ルートがない場合
rootがnullまたはundefinedの場合、Portalは何も表示しません。
<Portal root={null} children={<span>Won't appear</span>} />
// renders nothingDefer
実際の値が準備されている間(例えば、Promise の解決を待っている場合や、Promise 以外の値を受信するシグナルを待っている場合など)、初期のプレースホルダー値を表示します。
タイプ署名
const Defer: PC<{
initial?: any;
value: Signal<any>;
}>プロパティ
| プロパティ | 型 | 必須 | 説明 |
|---|---|---|---|
initial | any | いいえ | value に Promise が含まれているか、まだ準備が整わない場合にレンダリングされるプレースホルダーコンテンツ。 |
value | Signal<any> | はい | 値が監視されるシグナル。シグナルの解決値が Promise でない場合、initial のコンテンツが置き換えられます。 |
Defer Promise
import type { PC } from 'preffx';
const SlowData: PC = (_, { signal, Defer }) => {
const data = signal(
new Promise<string>((resolve) =>
setTimeout(() => resolve('Loaded!'), 2000)
)
);
return (
<Defer
initial={<div>Loading...</div>}
value={data}
/>
);
};レンダリングシーケンス:
- すぐに
Loading...と表示されます。 - 2秒後、Promiseが解決されると「読み込み完了!」と表示されます。
Defer 単純な(約束ではない)値
シグナルが既にプレーンな値(Promiseではない)を保持している場合、Deferはそれを即座にレンダリングします。initialはスキップされます。
<Defer
initial="Waiting..."
value={signal('Real content')}
/>
// Immediately renders "Real content"Promiseからプレーンな値へのリアクティブな切り替え
シグナルがPromiseからプレーンな値に変化すると、deferはプレースホルダーから実際のコンテンツに切り替わります。
const DynamicDefer: PC = (_, { signal, effect, Defer, onMount }) => {
const value = signal(new Promise(() => {})); // pending promise
onMount(() => {
setTimeout(() => {
value.value = 'Finally resolved!';
}, 1000);
});
return <Defer initial="Loading..." value={value} />;
};Defer による配列の子要素の処理
defer は、JSX ノードの配列を含むあらゆるコンテンツで機能します。
const items = signal([
<span id="a">Alpha</span>,
<span id="b">Beta</span>,
]);
return (
<Defer
initial={[<div>Loading...</div>]}
value={items}
/>
);
// Renders <span>Alpha</span><span>Beta</span> immediatelyクリーンアップ
親コンポーネントが破棄されると、defer は内部シグナル追跡を自動的にクリーンアップします。