Context API

コンテキストAPIは、データや関数をプロパティとして渡したり、たくさんのイベントをディスパッチしたりすることなく、コンポーネント同士で'会話'するための仕組みを提供します。これは高度ですが、便利な機能です。

Mapbox GLのマップを使ったこのアプリの例を見てみましょう。<MapMarker>を使用してマーカーを表示したいのですが、ベースとなるMapboxインスタンスへの参照を各コンポーネントのプロパティとして渡したくありません。

コンテキストAPIはsetContextgetContextに分かれます。もしコンポーネントがsetContext(key, context)を呼ぶと、どのコンポーネントでもconst context = getContext(key)でコンテキストを取得することができます。

まずはコンテキストを設定してみましょう。Map.svelteでは、svelteからsetContextをインポートし、mapbox.jsからkeyをインポートして、setContextを呼び出します。

import { onMount, setContext } from 'svelte';
import { mapbox, key } from './mapbox.js';

setContext(key, {
	getMap: () => map
});

コンテキストオブジェクトはなんでも構いません。lifecycle functionsのように、setContextgetContextはコンポーネントの初期化時に呼び出されなければいけません。それより後 (例えば onMount の中) で呼び出すとエラーをスローします。この例では、コンポーネントがマウントされるまでmapは作成されないので、このコンテキストオブジェクトにはmap自体ではなくgetMap関数が含まれています。

一方、MapMarker.svelteでは、Mapboxインスタンスへの参照を取得できるようになりました。

import { getContext } from 'svelte';
import { mapbox, key } from './mapbox.js';

const { getMap } = getContext(key);
const map = getMap();

これでマーカーをマップに追加することができるようになりました。

<MapMarker>のより完成度の高いバージョンでは削除やプロパティの変更も扱えますが、この場ではコンテキストのデモンストレーションに留めておきます。

Context keys

mapbox.jsにはこの一行が含まれています。

const key = {};

どんなものでもキーとして使うことができます(例えばsetContext('mapbox', ...)のように)。文字列を使用することの欠点は、異なるコンポーネントライブラリが誤って同じものを使ってしまう可能性があることです。オブジェクトリテラルを使えば、どんな状況でもキーが衝突しないことが保証されます(オブジェクトは自身に対する参照の等価性しか持ちません。すなわち{} !== {}に対し"x" === "x"となります)。たとえ複数の異なるコンテキストが多くのコンポーネントレイヤーを超えて動作している場合であっても、です。

Contexts vs. stores

コンテキストとストアは似ているように見えます。ストアはアプリのどの部分でも使用できるのに対し、コンテキストはコンポーネントとその子孫のみが利用できるという点で異なります。これは、ある状態が他の状態に干渉することなく、コンポーネントの複数のインスタンスを使用したい場合に便利です。

実際には、この2つを一緒に使うこともあるかもしれません。コンテキストはリアクティブではないので、時間の経過とともに変化する値はストアとして表現する必要があります。

const { these, are, stores } = getContext(...);


		
loading editor...

Console

loading Svelte compiler...


		
loading editor...

Compiler options

result = svelte.compile(source, {
generate:
});


		
loading editor...