Finite State Machines in Vue 3

A presentation at VueConf Toronto in November 2020 in by Sarah Dayan

Slide 1

Slide 1

Finite State Machines in Vue 3

Slide 2

Slide 2

Slide 3

Slide 3

Slide 4

Slide 4

In web apps, state is everywhere.

Slide 5

Slide 5

State can be strings, numbers 1 <template> 2 <p>My name is {{ name }} and I’m {{ age }} years old.</p> 3 4 5 6 7 8 9 10 11 12 13 14 15 16 </template> <script> import { ref, computed } from “vue”; export default { setup() { const name = ref(“Sarah Dayan”); const birthYear = ref(1990); const age = computed(() => new Date().getYear() - birthYear.value); return { name, age }; }, };

Slide 6

Slide 6

State can be arrays, objects 1 <template> 2 <p>My favorite snacks are:</p> 3 4 5 6 7 8 9 10 11 12 13 14 15 16

<ul> <li v-for=”{ label, emoji } in food” :key=”label”> {{ label }} ({{ emoji }}) </li> </ul> </template> <script> import { ref } from “vue”; export default { setup() { const food = ref([ { label: “croissant”, emoji: ” 🥐 ” },

Slide 7

Slide 7

State can be booleans 1 <template> 2 <p>I have a secret…</p> 3 4 5 6 7 8 9 10 11 12 13 14 15 16

<p v-if=”shouldReveal”>{{ secret }}</p> <button v-else @click=”reveal”>Please tell!</button> </template> <script> import { ref } from “vue”; export default { setup() { const shouldReveal = ref(false); const secret = ref(“Don’t repeat it!”); function reveal() { shouldReveal.value = true;

Slide 8

Slide 8

Data fetching with loading state 1 export default { 2 setup() { 3 const { data, loading, error } = toRefs( 4 reactive({ data: [], loading: true, error: null }) 5 ); 6 7 fetch(“https://example.com/api/users”) 8 .then((users) => (data.value = users)) 9 .catch((err) => (error.value = err)) 10 .finally(() => (loading.value = false)); 11 12 return { data, loading, error }; 13 }, 14 };

Slide 9

Slide 9

Data fetching with loading state 1 export default { 2 setup() { 3 const { data, loading, error } = toRefs( 4 reactive({ data: [], loading: true, error: null }) 5 ); 6 7 fetch(“https://example.com/api/users”) 8 .then((users) => (data.value = users)) 9 .catch((err) => (error.value = err)) 10 .finally(() => (loading.value = false)); 11 12 return { data, loading, error }; 13 }, 14 };

Slide 10

Slide 10

Data fetching with loading state 1 export default { 2 setup() { 3 const { data, loading, error } = toRefs( 4 reactive({ data: [], loading: true, error: null }) 5 ); 6 7 fetch(“https://example.com/api/users”) 8 .then((users) => (data.value = users)) 9 .catch((err) => (error.value = err)) 10 .finally(() => (loading.value = false)); 11 12 return { data, loading, error }; 13 }, 14 };

Slide 11

Slide 11

Data fetching with loading state 1 export default { 2 setup() { 3 const { data, loading, error } = toRefs( 4 reactive({ data: [], loading: true, error: null }) 5 ); 6 7 fetch(“https://example.com/api/users”) 8 .then((users) => (data.value = users)) 9 .catch((err) => (error.value = err)) 10 .finally(() => (loading.value = false)); 11 12 return { data, loading, error }; 13 }, 14 };

Slide 12

Slide 12

Data fetching with loading state 1 export default { 2 setup() { 3 const { data, loading, error } = toRefs( 4 reactive({ data: [], loading: true, error: null }) 5 ); 6 7 fetch(“https://example.com/api/users”) 8 .then((users) => (data.value = users)) 9 .catch((err) => (error.value = err)) 10 .finally(() => (loading.value = false)); 11 12 return { data, loading, error }; 13 }, 14 };

Slide 13

Slide 13

Data fetching with loading state 1 <template> 2 <div v-if=”loading”>Loading users…</div> 3 <table v-if=”data.length”> 4 <td :key=”user.id” v-for=”user in data”> 5 <div>{{ user.name }}</div> 6 <div>{{ user.email }}</div> 7 </td> 8 </table> 9 <div v-if=”error”>Uh-ho! Something unexpected happened.</div> 10 </template>

Slide 14

Slide 14

Data fetching with loading state 1 <template> 2 <div v-if=”loading”>Loading users…</div> 3 <table v-if=”data.length”> 4 <td :key=”user.id” v-for=”user in data”> 5 <div>{{ user.name }}</div> 6 <div>{{ user.email }}</div> 7 </td> 8 </table> 9 <div v-if=”error”>Uh-ho! Something unexpected happened.</div> 10 </template>

Slide 15

Slide 15

Data fetching with loading state 1 <template> 2 <div v-if=”loading”>Loading users…</div> 3 <table v-if=”data.length”> 4 <td :key=”user.id” v-for=”user in data”> 5 <div>{{ user.name }}</div> 6 <div>{{ user.email }}</div> 7 </td> 8 </table> 9 <div v-if=”error”>Uh-ho! Something unexpected happened.</div> 10 </template>

Slide 16

Slide 16

Data fetching with loading state 1 <template> 2 <div v-if=”loading”>Loading users…</div> 3 <table v-if=”data.length”> 4 <td :key=”user.id” v-for=”user in data”> 5 <div>{{ user.name }}</div> 6 <div>{{ user.email }}</div> 7 </td> 8 </table> 9 <div v-if=”error”>Uh-ho! Something unexpected happened.</div> 10 </template>

Slide 17

Slide 17

Data fetching with loading state 1 const { data, loading, error } = toRefs( 2 reactive({ data: [], loading: true, error: null }) 3 4 5 6 7 8 9 10 11 ); fetch(“https://example.com/api/users”) .then((users) => (data.value = users)) .catch((err) => (error.value = err)); // loading.value is still true return { data, loading, error };

Slide 18

Slide 18

Slide 19

Slide 19

Imperative code is more error-prone.

Slide 20

Slide 20

We should only control the what, not the how.

Slide 21

Slide 21

We should only control the what, not the how.

Slide 22

Slide 22

State machines.

Slide 23

Slide 23

Slide 24

Slide 24

Slide 25

Slide 25

Slide 26

Slide 26