DEV Community

Cover image for Learn state management by making a (silly) story generator (Learn Modulo.js - Part 3 of 10)
michaelb
michaelb

Posted on

Learn state management by making a (silly) story generator (Learn Modulo.js - Part 3 of 10)

👋 Welcome back! Didn't catch Part 1? No worries, you can start at the beginning, or just plunge in here!

Introducing: The SillyStory Web Component

Our task in this tutorial will be to build a story generating component. This will give us lots of practice with using State. Last time we ended with a snippet a little like the one below. However, in this tutorial, we've changed the "Template" to show a silly story, generated by words given as Props. This allows the component to be "re-used" with different silly words. To begin this tutorial, copy and paste the following into a new file, and open in your browser:

<template Modulo>
    <Component name="SillyStory">
        <Props
            name
            count
            verb
            animal
            noun
        ></Props>
        <Template>
            <p>Professor {{ props.name }} had over {{ props.count }} degrees in {{ props.noun }}-{{ props.verb }}ing from {{ props.name }} University. One day, the Professor decided to travel to {{ props.name }} City, a magical land filled with talking {{ props.animal }}s.</p>
            <p>The quest was bold: The Professor was to teach {{ props.count }} {{ props.animal }}s how to {{ props.verb }} a {{ props.noun }}.</p>
        </Template>
    </Component>
</template>
<script src="https://unpkg.com/mdu.js"></script>
<x-SillyStory name="Nonsense" count="one million" verb="toot" animal="duck" noun="kazoo"></x-SillyStory>
Enter fullscreen mode Exit fullscreen mode

Introducing Part 3

Image showing Dev Tools open on a silly story generator with the code of an input showing it on the screen

This tutorial introduces one of the central concepts of not just Modulo, but many popular modern web frameworks, including React.js, Vue.js and others. This is the concept of State.

So far, our components have been static and unchanging. They can't do a lot. They might be useful for refactoring HTML into more D.R.Y. ("Don't Repeat Yourself"), reusable components, but not much more than that. They can't validate form data, use APIs, or be used in complete applications. In fact, they can't have any sort of interaction whatsoever, or any dynamic or changing content. We want reactivity in our components.

You can think of the concept of these state variables as being like "buckets for data", or "blanks that can be filled in". They allow us to "give a name" to refer to data, allowing for more generic code and templates to be written. That way, your component can have changing content: Change the state, see the result!

Step 1: Adding a State part

State Component Parts are defined much like Props, except that instead of just listing the attribute names, initial values must be provided as defaults. We can "transform" our Props into State like this:

<State
    name="Nonsense"
    verb="toot"
    noun="kazoo"
    count="one hundred"
    animal="duck"
></State>
Enter fullscreen mode Exit fullscreen mode

Step 2: Include State in your template

State can be inserted into your Template just like Props. In fact, all we need to do to get the previous example working again with our new-fangled State part is to just change props to state, as follows:

<Template>
    <p>Professor {{ state.name }} had over {{ state.count }} degrees in {{ state.noun }}-{{ state.verb }}ing from {{ state.name }} University. One day, the Professor decided to travel to {{ state.name }} City, a magical land filled with talking {{ state.animal }}s.</p>
    <p>The quest was bold: The Professor was to teach {{ state.count }} {{ state.animal }}s how to {{ state.verb }} a {{ state.noun }}.</p>
</Template>
Enter fullscreen mode Exit fullscreen mode

If you perform these two steps correctly, your story generator will still work, except it won't need the attributes any more, since the data will come from "state". This means you should remember to delete them from the use of the component at the bottom, so it looks like: <x-SillyStory></x-SillyStory>

Introducing: The state.bind directive

So far, we can manually change state in the source code, but our app is still not interactive, since the actual users (that is, non-coders) cannot change the state. This is where "binding" comes into play, where user input is "bound" to state, such that users of your app can also modify state variables while using your app.

To "bind", we'll need to use a directive. A directive is a type of HTML attribute. You can recognize directive by spotting certain special characters in the attribute name. There are other directives, but for now, we'll only care about one directive: [state.bind].

What does this mean? It will "sync up" the input with the state after every keystroke. The binding is "two-way": Modifying state modifies the input, and modifying the input modifies state. For example, <input [state.bind] name="animal" /> is an input HTML tag with a [state.bind] directive. In this case, it will "bind" the input to the "animal" state variable.

Step 3: Bind our first input to state

In our case, to add an labelled input that's bound to our State variable "animal", we can do this:

<Template>
    <p><label>Animal: <input [state.bind] name="animal" /></label></p>
    <!-- story goes here... -->
</Template>
<State
    animal="ducks"
></State>
Enter fullscreen mode Exit fullscreen mode

Step 4: Finishing up and testing it out

Now, "rinse and repeat" for all the remaining inputs:

<p><label>Name: <input [state.bind] name="name" /></label></p>
<p><label>Verb: <input [state.bind] name="verb" /></label></p>
<p><label>Noun: <input [state.bind] name="noun" /></label></p>
<p><label>Count: <input [state.bind] name="count" /></label></p>
<p><label>Animal: <input [state.bind] name="animal" /></label></p>
Enter fullscreen mode Exit fullscreen mode

Here's how to test it out: First, make sure all the inputs start filled in (if you had a typo in a name, it could cause this to not show -- check the Dev Tools for errors!). Then, try editing each of your different inputs. Do you see how it "reacts" to your typing, automatically re-rendering each story as you type new silly words?

<x-SillyStory> - Complete Example

Combining it all, we get the following (silly) results:

<template Modulo>
    <Component name="SillyStory">
        <Template>
            <p><label>Name: <input [state.bind] name="name" /></label></p>
            <p><label>Verb: <input [state.bind] name="verb" /></label></p>
            <p><label>Noun: <input [state.bind] name="noun" /></label></p>
            <p><label>Count: <input [state.bind] name="count" /></label></p>
            <p><label>Animal: <input [state.bind] name="animal" /></label></p>
            <p>Professor {{ state.name }} had over {{ state.count }} degrees in {{ state.noun }}-{{ state.verb }}ing from {{ state.name }} University. One day, the Professor decided to travel to {{ state.name }} City, a magical land filled with talking {{ state.animal }}s.</p>
            <p>The quest was bold: The Professor was to teach {{ state.count }} {{ state.animal }}s how to {{ state.verb }} a {{ state.noun }}.</p>
        </Template>
        <State
            name="Nonsense"
            verb="toot"
            noun="kazoo"
            count="one hundred"
            animal="duck"
        ></State>
    </Component>
</template>
<script src="https://unpkg.com/mdu.js"></script>
<x-SillyStory></x-SillyStory>
Enter fullscreen mode Exit fullscreen mode

State & beyond

Some food for thought: How can this be used in UI development? Think about State in every day web applications: Forms, tabs, modal pop-up interfaces... Really, anything that is dynamic or reactive. All of these are State changing. State can hold CSS classes, CSS values, be bound to sliders and other types of inputs, and so much more! In other words, we have a lot more to cover on State, but we'll save that for Part 4. Be sure to follow for more tutorials like this, and, as always, feel free to ask questions or suggestions in the comments.

Top comments (0)