Svelte にて 子A (イベント発火) → 親 (受取り) → 子B (処理) という仕組みを作る

子の Component から発火されるイベントを親に知らせることは簡単だったけど、
親から子の Component に何か処理させる方法がわからなくて時間がかかった。

ファイル構成

src/
  ├ ViewModels/
  │    ├ ChildA.svelte
  │    └ ChildB.svelte
  └ Views/
       └ Parent.svelte

やりたいこと

  • ChildA.svelte でイベント発火。(今回はクリックイベント)
    • Parent.svelte で ChildA.svelte からのイベントを受取る。
      • Parent.svelte から ChildB.svelte の中で定義した関数を呼び出す。

パターン 1 : 子A → 親

ChildA.svelte (コールバック)

  • コールバックでイベント伝播させる。
  • Event オブジェクトは受け取れない。
  • 可読性は良い方だと思う。
<script>
	export let didClick = () => {}

	function onClick() {
		didClick()
	}
</script>

<style lang="scss"></style>


<button on:click={ onClick }>Fire</button>

Parent.svelte

  • ChildA.svelte 内のクリックイベントを fire() で受け取る。
<script>
	import ChildA.svelte from './../ViewModels/ChildA.svelte'

	function fire() {
		console.log('parent: fire')
	}
</script>

<style lang="scss"></style>


<ChildA didClick={ fire }/>

パターン 2 : 子A → 親

ChildA.svelte (createEventDispatcher)

<script>
	import { createEventDispatcher } from 'svelte'

	const _dispatch = createEventDispatcher()

	function click() {
		_dispatch('didClick')
	}
</script>

<style lang="scss"></style>


<button on:click={ click }>Fire</button>

Parent.svelte

  • ChildA.svelte 内のクリックイベントを fire() で受け取る。
  • dispatch を使う時は didClick={} → on:didClick={} になる
  • Parent.svelte 側は on: がついてるので可読性は良いかもしれない。
<script>
	import ChildA from './../ViewModels/ChildA.svelte'

	function fire(e) {
		console.log('parent: fire')
		console.log(e)
	}
</script>

<style lang="scss"></style>


<ChildA on:didClick={ fire }/>

親 → 子B

ChildB.svelte

<script>
	import { onMount } from 'svelte'
	import { createEventDispatcher } from 'svelte'

	const _dispatch = createEventDispatcher()

	let counter = 0

	function _methods() {
		return {
			countUp: () => {
				_countUp()
			}
		}
	}

	function _countUp() {
		counter += 1
	}

	onMount(() => {
		_dispatch('didInit', _methods)
	})
</script>

<style lang="scss"></style>


<div>{ counter }</div>

Parent.svelte

  • ページ読み込み時に ChildB.svelte から didInit イベントを受け取る。
<script>
	import ChildB.svelte from './../ViewModels/ChildB.svelte'

	let childBEvent = null

	function didInitChildB(e) {
		childBEvent = e
	}
</script>

<style lang="scss"></style>


<ChildB on:didInit={ didInitChildB }/>

子A → 親 → 子B

Parent.svelte

  • ChildA.svelte はここではパターン 1 (コールバック) のほうで実装。
  • ChildA.svelte と ChildB.svelte は上記参考。
  • ChildA.svelte でクリックされたイベントを fire() で受け取り、ページ読み込み時に受け取った ChildB.svelte の CustomEvent オブジェクトの detail を使って countUp() を呼び出す。
<script>
	import ChildA from './../ViewModels/ChildA.svelte'
	import ChildB from './../ViewModels/ChildB.svelte'

	let childBEvent = null
	
	function didInitChildB(e) {
		childBEvent = e
	}
	
	function fire() {
		if (childBEvent) {
			childBEvent.detail().countUp()
		}
	}
</script>

<style lang="scss"></style>


<ChildA didClick={ fire }/>
<ChildB on:didInit={ didInitChildB }/>