Svelte: Communication Between Components

Written by the_rock | Published 2020/02/26
Tech Story Tags: svelte | javascript | events | components | tutorial | software-development | programming | react

TLDR Svelte: Communication Between Components - a callback and event dispatcher. A callback method is the way you do communication between components in React.js. A parent component passes a callback method to a child, and then, child invokes it. In this type of communication, a component triggers some events on it's self and then someone (a parent component) will listen to it and do something once triggered. Sveltte allows you to use both of them (as Vue does)via the TL;DR App

In this mini-tutorial we are going to see two different ways of communication between components in Svelte 3: a callback passed as prop and event dispatcher.

Callback communication

A callback method is the way you do communication between components in React. A parent component passes a callback method to a child, and then, child invokes it.

Events based communication

This one is a typical way you do communication in Vue.js (although, it also allows you to use callbacks as props). In this type of communication, a component triggers some events on it's self, and then someone (a parent component) will listen to it and do something once triggered.

Svelte

Svelte allows you to use both of them (as Vue does).
Here we have a simple Guess a Number project. It consist of 3 components: App (parent component), SecretValue (events based communication) and GuessComponent (prop passing). Under the project, there is a detailed explanation of both methods with small snippets, but I think it's better to see a complete project in order to understand better how it works.

App.svelte

<script>
	import { onMount } from "svelte";
	import SecretValue from "./SecretValue.svelte";
	import GuessComponents from "./GuessComponent.svelte";

	let secret = null;
	let showSecret = false;

	const getRandomInt = (min, max) => {
		min = Math.ceil(min);
		max = Math.floor(max);
		return Math.floor(Math.random() * (max - min + 1)) + min;
	}

	const guessNumber = () => {
		showSecret = false;
		secret = getRandomInt(1, 10);
		console.log(secret);
	}

	const onGuess = value => {
		if(value == secret){
			showSecret = true;
		}
	}

	onMount(() => {
		guessNumber();
	});

</script>

<main>
	<h1>Guess a Number</h1>
	<SecretValue showSecret={showSecret} secret={secret} on:restart={guessNumber} />
	<GuessComponents onGuess={onGuess} />
</main>

<style>
	main {
		text-align: center;
		padding: 1em;
		max-width: 240px;
		margin: 0 auto;
	}

	h1 {
		color: #ff3e00;
		text-transform: uppercase;
		font-size: 4em;
		font-weight: 100;
	}

	@media (min-width: 640px) {
		main {
			max-width: none;
		}
	}
</style>

SecretValue.svelte

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

    export let secret = null;
    export let showSecret = false;

    const dispatch = createEventDispatcher();
    
    const playAgain = () => {
        dispatch("restart");
    }

</script>

<div>
    <p>{showSecret ? secret : "???"}</p>
    { #if showSecret }
        <p>You won</p>
        <button on:click={playAgain}>Play again</button>
    { /if }
</div>

GuessComponent.svelte

<script>
    let guessValue = "";
    export let onGuess = null;

    const guess = e => {
        onGuess(guessValue);
    }
</script>

<div>
    <input bind:value={guessValue} />
    <button on:click={guess}>Guess</button>
</div>

Explanation: Event Dispatcher

Let's take a look at SecretValue.svelte. In order to trigger events we must create a dispatcher:
    import { createEventDispatcher } from 'svelte';
    const dispatch = createEventDispatcher();
Now, you can use dispatch object to trigger events on the component:
dispatch("event_name");
To listen to this event in parent component just add
on:event_name
on child and pass a method as a parameter (see App.svelte). The data you want to trigger will be passed as a detail property of the event.

Complete snippet:

<!-- Parent.svelte -->
<script>
    import Child from "./Child.svelte";

    const sayHello = (e) => {
        alert("Hello " + e.detail);
    }
</script>

<Child on:hello={sayHello} />

<!-- Child.svelte -->
<script>
    import { createEventDispatcher } from 'svelte';

    const dispatch = createEventDispatcher();
    
    const triggerEvent = () => {
        dispatch("hello", "Rock");
    }
</script>

<button on:click={triggerEvent}>Call callback</button>

Explanation: Callback

The callback method is very simple: create a function on parent component and pass it to child:
<!-- Parent.svelte -->
<script>
    import Child from "./Child.svelte";
    const callback = (message) => {

    }
</script>

<Child callback={callback} />

<!-- Child.svelte -->
<script>
    export let callback;

    const callCallback = () => {
        callback("test message");
    }
</script>

<button on:click={callback}>Call callback</button>

Which is better?

Personally, I prefer an event-based communication, but I don't think it's actually better. The only problem with props is that it can cause problems if badly managed, but normally, both of them are ok. In real life, I would opt for storage.
You may also be interested in:

Written by the_rock | Software Developer from monday to friday (Healthcare sector), Game Developer in free time
Published by HackerNoon on 2020/02/26