5
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

SvelteAdvent Calendar 2024

Day 3

Svelte5でコンポーネントからexportした関数を呼び出し側から実行する方法

Last updated at Posted at 2024-12-02

コード

早い話ここを見てください。

で終わると記事にしている意味がないので説明します。

最終的にやりたいのは、

  • コンポーネントを$stateで書き換わる値によって出し分ける
  • 出し分けた上で、出し分けられたコンポーネントがexportしている関数を実行する

です。
いくつか前持って知っておく必要のあることがあるので順番に説明します。

Svelteではコンポーネントから関数をexportできる

Component.svelte
<script lang="ts">
	export const execute = () => {
		console.log('test')
	};
</script>

または

Component.svelte
<script lang="ts">
	export function execute(){
		console.log('test')
	};
</script>

このようにコンポーネントを作っておくと、Component.svelteを呼び出す側でexecute関数を実行することができます。

モーダルコンポーネントにcloseを生やしておく などの使い方ができます。

Component型

Svelte4ではSvelteComponent型でしたが、Svelte5からはComponent型に変わっています。

定義は

interface Component<
  Props extends Record<string, any> = {},
  Exports extends Record<string, any> = {},
  Bindings extends keyof Props | "" = string
>

このようになっており、一つ目の型引数にはprops、二つ目の型引数にはイベント、三つ目の型引数にはbindするpropsの型を指定します。

今回は出し分けるコンポーネントがどちらも同じ名前の関数をexportしておいて欲しいので、出し分け対象のコンポーネントが同じ名前の関数を持っていることを型で保証するためにこちらを使用します。

Svelte5からコンポーネントはクラスではなく関数になった

Svelte4まではコンポーネントはクラスでした。

Svelte5からは関数になったので、コンポーネントからexportした関数の型を得るためにはReturnTypeを使用する必要があります。

今回で言うと、bind:this={bindButton}するときのbindButtonの型にこれを使用します。

説明終わり

ここまでで説明は終わりです。
実際のコードを見ていきましょう。

AddButton.svelte

AddButton.svelte
<script lang="ts">
	let {
		count = $bindable(0),
	}: {
		count: number;
	} = $props();

	export const execute = () => {
		count += 1;
	};
</script>

<button type="button" onclick={() => execute()}
	>{count}からカウントを+1する</button
>

bindableなprops、countを受け取ります。

また、execute関数をコンポーネント内にあるボタン、もしくは外部から実行することができます。

何でこんなことをしているのかわからないですが、どうやらボタンのクリック以外の任意のタイミングcountを増やしたいみたいです。

bindableなcountにしているのでこの例ではexecute関数をexportする意味は全くないのですが、あまり複雑なコンポーネントを作ろうとすると記事を書くのが面倒になるので今回はこれで許してください…。

MinusButtonの方はcountを1マイナスします。

+page.svelte

AddButtonMinusButtonの二つをimportしています。

script部分はこうなっています。

<script lang="ts">
	import type { Component } from 'svelte';
	import AddButton from '../lib/AddButton.svelte';
	import MinusButton from '../lib/MinusButton.svelte';

	let count = $state(0);
	let componentName = $state<'AddButton' | 'MinusButton'>('AddButton');
	let ButtonComponent = $derived<
		Component<
			{
				count: number;
			},
			{
				execute: () => void;
			}
		>
	>(componentName === 'AddButton' ? AddButton : MinusButton);

	let bindButton = $state<ReturnType<typeof ButtonComponent>>();
</script>

<h1>Examples</h1>

<h2>bindされたコンポーネントのexecuteを実行する</h2>
<label for="component-name">コンポーネント</label>
<select id="component-name" bind:value={componentName}>
	<option value="AddButton">AddButton</option>
	<option value="MinusButton">MinusButton</option>
</select>
<ul>
	<li><ButtonComponent bind:count bind:this={bindButton}></ButtonComponent></li>
	<li>
		<button type="button" onclick={() => bindButton?.execute()}
			>コンポーネントのexecuteを実行</button
		>
	</li>
</ul>

<p>count: {count}</p>
  • countをbindしたいので$stateで持っている
  • componentName$stateで管理されており、セレクトボックスの値とbindされている
  • componentNameを元にButtonComponentAddButtonMinusButtonを出し分けている
  • ButtonComponentbind:this={bindButton}が書かれている
  • bindButton?.excute()がボタンに書かれている

結構複雑ですが、これまでに説明してきた内容をフル活用すると結構いろんなことができるのではないか?という気持ちになってきているのではないでしょうか。

ButtonComponentで動的に出し分けられたコンポーネントに対してbindしておくと、そのコンポーネントがexportしている関数を実行することができます。

これを応用すると、例えばトーストのような配列から複数の同じ種類のコンポーネントを表示する場合、それらをbindしておき、その中の一つに対して生やしておいたcloseを実行することで任意のトーストだけを画面上から消し去ったりすることができます。

もちろん配列にstateを持っておいて表示非表示を切り替えたり単純に配列から消すなどの対応でもうまく動きますが、トーストを画面から消すタイミングでAPIにリクエストを投げてDBと同期する みたいな処理も今回の実装であれば簡単に対応することができます。

ちょっと難しい使い方にはなりますが、応用の幅は広そうです。
機会があれば試してみてください。

5
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
5
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?