Skip to content

組み込みコンポーネント

PrefXには、各コンポーネントの2番目の引数(utils)からアクセスできる4つの組み込みコンポーネントが付属しています。これらは、追加の依存関係なしに一般的なUIパターンを解決します。

コンポーネント目的利用可能性
Forリアクティブアイテムを含むリストをレンダリングしますutils.For
Catchレンダリングエラーをキャッチして処理しますutils.Catch
Portal子要素を別のDOMノードにレンダリングしますutils.Portal
Defer値が準備できるまでプレースホルダーを表示しますutils.Defer

For

リアクティブリストレンダラー。アイテムのシグナル(またはプレーンな配列)を受け取り、変更されたアイテムのみを再レンダリングします。リストが空の場合に表示するオプションのfallbackをサポートしています。

タイプ署名

typescript
const For: PC<{
    items: Signal<any[]>;
    callback: (item: any) => any;
    fallback?: any;
}>

プロパティ

プロパティ必須説明
itemsSignal<any[]>はいアイテムの配列を保持するシグナル。シグナルが変更されると、For はリストを効率的に調整します。追加されたアイテムはレンダリングされ、削除されたアイテムはクリーンアップされます。
callback(item: any) => anyはい各アイテムに対して呼び出される関数。そのアイテムに対してレンダリングする JSX を返します。返された値はキャッシュされ、アイテムの値自体が変更された場合にのみ再計算されます。
fallbackanyいいえ配列が空の場合にレンダリングされるコンテンツ。

使用法

tsx
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が自動的に更新されます。

tsx
// 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

配列内の信号要素

配列内の各要素は、それ自体が信号となり得ます。要素の信号値が変化すると、その要素の出力のみが再計算されます。

tsx
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のエラー境界にヒントを得ています。

タイプ署名

typescript
const Catch: PC<{
    fallback?: any;
    children?: any | any[];
}>

プロパティ

プロパティ必須説明
childrenany | any[]いいえレンダリングする子要素。子要素が Error インスタンスである場合(または Error インスタンスを返す場合)、エラーが捕捉されます。
fallbackanyいいえエラーが捕捉されたときに表示するコンテンツ。静的な値、または { errors, ...extraProps } を受け取る 関数コンポーネント を指定できます。

静的コンテンツとしてフォールバックする

tsx
import type { PC } from 'preffx';

const App: PC = (_, { Catch }) => {
    return (
        <div>
            <Catch fallback={<div>Something went wrong</div>}>
                <MaybeBrokenComponent />
            </Catch>
        </div>
    );
};

フォールバックを関数コンポーネントとして扱う

フォールバック関数は、errors(捕捉されたエラーの配列)と、<Catch>に渡されるその他のプロパティを受け取ります。

tsx
const App: PC = (_, { Catch }) => {
    return (
        <Catch
            fallback={({ errors }) => (
                <div class="error-fallback">
                    <h3>{errors.length} error(s) occurred</h3>
                </div>
            )}
        >
            <UnstableComponent />
        </Catch>
    );
};

予備選手に余分な小道具を渡す

tsx
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ノードにレンダリングします。モーダル、ツールチップ、ドロップダウン、通知などに便利です。

タイプ署名

typescript
const Portal: PC<{
    root: HTMLElement;
    children?: any | any[];
}>

プロパティ

プロパティ必須説明
rootHTMLElementはい子要素をレンダリングする対象のDOM要素。
childrenany | any[]いいえポータルルート内にレンダリングするコンテンツ。

基本的な使い方

tsx
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>
    );
};

ポータル内のリアクティブなコンテンツ

ポータル内の子は完全に反応します。信号により自動的に更新されます。

tsx
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() によってポータルルートをクリアします。

ルートがない場合

rootnullまたはundefinedの場合、Portalは何も表示しません。

tsx
<Portal root={null} children={<span>Won't appear</span>} />
// renders nothing

Defer

実際の値が準備されている間(例えば、Promise の解決を待っている場合や、Promise 以外の値を受信するシグナルを待っている場合など)、初期のプレースホルダー値を表示します。

タイプ署名

typescript
const Defer: PC<{
    initial?: any;
    value: Signal<any>;
}>

プロパティ

プロパティ必須説明
initialanyいいえvalue に Promise が含まれているか、まだ準備が整わない場合にレンダリングされるプレースホルダーコンテンツ。
valueSignal<any>はい値が監視されるシグナル。シグナルの解決値が Promise でない場合、initial のコンテンツが置き換えられます。

Defer Promise

tsx
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}
        />
    );
};

レンダリングシーケンス:

  1. すぐにLoading...と表示されます。
  2. 2秒後、Promiseが解決されると「読み込み完了!」​​と表示されます。

Defer 単純な(約束ではない)値

シグナルが既にプレーンな値(Promiseではない)を保持している場合、Deferはそれを即座にレンダリングします。initialはスキップされます。

tsx
<Defer
    initial="Waiting..."
    value={signal('Real content')}
/>
// Immediately renders "Real content"

Promiseからプレーンな値へのリアクティブな切り替え

シグナルがPromiseからプレーンな値に変化すると、deferはプレースホルダーから実際のコンテンツに切り替わります。

tsx
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 ノードの配列を含むあらゆるコンテンツで機能します。

tsx
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 は内部シグナル追跡を自動的にクリーンアップします。

Apache License 2.0に基づいて公開されています。