3
0

この記事は2部構成です。
part1はこちら

前回のpart1でほぼ終わっているのですが、stateを管理するcreateLoading関数とSkeletonコンポーネントを作成していないので作成していきましょう。

createLoading

createLoading.svelte.tsというファイル名で以下を作成します。

import { getContext, hasContext, setContext } from 'svelte';

type Context = {
	state: boolean;
};

export const createLoading = (symbol: symbol) => {
	if (hasContext(symbol)) {
		return getContext<Context>(symbol);
	}
	let loading = $state(true);
	const rune = {
		get state() {
			return loading;
		},
		set state(v) {
			loading = v;
		}
	};
	setContext<Context>(symbol, rune);
	return rune;
};
export const getLoadingContext = (key: symbol) => getContext<Context>(key);

ひとつのローディンググループごとにユニークなキーを持たせてそれに関連するスケルトンの表示状態を一度に変更かつ他のローディングの状態に依存させたくないので、Symbolを使用します。

クソ便利ですね!

中身はざっくり説明するとContextにrunesを詰めて返しているだけです。

getLoadingContext関数を作成し、いちいちgetContextに型を渡さなくていいようにしています。

Skeleton

Skeleton.svelteを以下で作成します。

<script lang="ts">
	import type { Snippet } from 'svelte';
	import { createLoading } from './createLoading.svelte';

	let {
		key,
		time = 3000,
		children
	}: {
		key: symbol;
		time: number;
		children: Snippet;
	} = $props();

	let loading = createLoading(key);
	setTimeout(() => {
		loading.state = false;
	}, time);
</script>

{@render children()}

このコンポーネントの中身はやりたい実装に合わせて調整してください。

今回は要件にある

  • スケルトンの内部でさらに別のスケルトンを表示する場合、同期されないようにしたい(スケルトンごとにグルーピングしたい)

これが確認できればそれでいいので単純にtime秒待ってからloading.stateをfalse(loading終了)に書き換えています。

この例ではContextで下層のコンポーネントにstateを渡す関係でこういった微妙なコンポーネントが必要になってしまっていますが、実際はSymbolの管理や適切な命名によって気にならなくなると思います。

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0