Simple state management in JavaScript with Nanny State

0

Nanny State is a small library I wrote to make it easier to build state-based web applications using Vanilla JS. It’s similar to React, but with much less overhead and a new syntax to learn. It also uses a single application-wide state object instead of each individual component having its own state. It was inspired by HyperApp and has many similarities to Elm.

In this article, I’ll explain how Nanny State works, then demonstrate what it can do with a few examples.

Nanny State uses a one-way data flow model, consisting of 3 parts:

  • State – an object that stores all the application data
  • See – a function that returns an HTML string based on the current state
  • Update – a function which is the only way to change the state and restore the view

In Nanny State, the state is everything. The state object is the only source of truth for your application – every bit of application data is a property of that object. Even the event handlers used in the view are methods of the state object.

The view is a representation of the report in HTML format. It changes whenever the state changes and allows users to interact with the application.

The update function is the only way to change the state. It is a single entry point for updating status and ensures changes are deterministic, consistent, and predictable.

These 3 things are all you need to create an app in Nanny State. In fact, it can be summed up by asking the following 3 questions:

  1. What data should I store in my app? This will constitute the properties of the State object
  2. How do I want app data presented on the page? This will help you create the View function
  3. How does app data change when the user interacts with it? The Update function will be needed for this

Hello Nanny State!

The easiest way to see how Nanny State works is to write some code! We’ll start with a basic example and then try to make something a little more complex.

The easiest way to run the following example is to use an online code editor such as CodePen, or you can run it locally by installing the nanny-state package using NodeJS.

Copy the following code into the JS section of CodePen:

import { Nanny,html } from 'https://cdn.skypack.dev/nanny-state';

const View = state => html`<h1>Hello ${state.name}h1>`

const State = {
  name: "Nanny State",
  View
}

const Update = Nanny(State)

This shows how the 3 parts of Nanny-State work together. Let’s take a close look at each part individually:

const View = state => html`<h1>Hello ${state.name}h1>`

The Nanny state uses µhtml to display HTML. The View function still accepts state objects as its only setting. He then uses the html function provided by µhtml to create HTML code based on the literal template provided as an argument.

Using a literal pattern means that we can use the ${variable} notation to insert the state properties into the view. In this example, we use it to insert the value of name property inside the

element.

const State = {
  name: "Nanny State",
  View
}

The State the object is where everything application data is stored. It includes all the properties and values ​​that will be displayed in the View and may change over the lifecycle of the application, such as the name property in this example.

Notice that View is also a property of State using shorthand object notation. Remember the state is everything – each part of the application is the property of the State.

const Update = Nanny(State)

The last line defines the Update function as the return value of the Nanny function. This can now be used to update the value of all properties in the State. In fact it is the only way that all the properties of the State can be updated. It also performs the initial rendering of the View based on the values ​​provided in the State. This means that a header will be displayed saying “Hello Nanny State” as seen in the CodePen below:

Hello Nanny State

This example is basically just a static page though. Let’s make it dynamic by adding an input box that allows the user to enter a name to whom they want to say hello. Update the code to look like this:

import { Nanny,html } from 'https://cdn.skypack.dev/nanny-state';

const View = state => html`<h1>Hello ${state.name}h1><input oninput=${state.changeName}>`

const changeName = event => Update({name: event.target.value})

const State = {
  name: "Nanny State",
  changeName,
  View
}

const Update = Nanny(State)

In this example, we added a element to the View. Event listeners are defined inline in the view, so in this example we have a oninput event listener attached to element. This will call the changeName event handler, which is a method of the state object, whenever an input is detected. This event listener needs to be set, so let’s take a closer look:

const changeName = event => Update({name: event.target.value})

This is a standard event handler written in Vanilla JS. It accepts an event object as a parameter as usual and when called we want to update the State object, so we use the Update function, because it is the only way to update the State.

The argument we provide to Update function is an object that contains all the properties we want to update in state and the respective new values. In this case, we want to update the name property to the value that was entered by the user in the input field, which is part of the event object and accessed using event.target.value. This will update the state with the new value of the input field and instantly render the page. Using µhtml for rendering means that only parts of the View that have actually changed are updated. This means that the new rendering after aState Updating is both efficient and incredibly fast.

And that’s it – your first Nanny State app! Try typing and you’ll see how quickly it reacts to user input…and all with just a few lines of code. You can see the code in the CodePen below:

Dynamic input

Nanny State makes it easy to write responsive state-based applications. As you can see, there is not a lot of code required to create a dynamic state-based application that reacts to user interaction. That’s the beauty of Nanny State.

True or false quiz

Now that we’ve seen a basic example, let’s try to make something a little more complex. We will use Nanny State to create a True or False quiz. Open a new pen on CodePen and follow.

We will start in the same way, by importing the Nanny State library:

import { Nanny,html } from 'https://cdn.skypack.dev/nanny-state'

Next we will create the State object and populate it with the initial property values ​​the game will use:

const State = {
  score: 0,
  index: 0,
  questions: [
    {question: "A Nanny State is a country where nannies are employed by the state", answer: false},
    {question: "Nanny State is also the name of a beer", answer: true},
    {question: "Mila Kunis and Ashton Kutcher employ 16 nannies to help raise their children", answer: false},
    {question: "The Nanny McPhee films are based on the Nurse Matilda books", answer: true},
    {question: "Nanny State uses the µhtml library for rendering", answer: true},
]
}

This object contains 3 properties:

  • score – this keeps track of how many questions the player answered correctly and starts at 0
  • index – this keeps track of which question the player is at and matches the last property which is the questions deploy.
  • questions – This is an array of objects with question and answer Properties. The question property is a string and the answer property is boolean

Now that we’ve created the data, let’s create the View to view this data:

const View = state => html`
<h1>True or False?h1>
<h2>Score: ${state.score}h2>
${state.index < state.questions.length ?
html`<p>${index + 1}) ${state.questions[state.questionNumber].question}p>
     <button onclick=${state.checkAnswer(true)}>TRUEbutton>
     <button onclick=${state.checkAnswer(false)}>FALSEbutton>`
:
html`<h2>Game Over, you scored ${state.score}h2>`
}`

It’s a bit more complicated View we saw in the previous example, but most should be pretty self-explanatory. After the title header, we display the score using the score property of the State object. Next, we use a ternary operator to fork the view. Since the view is written using template literals, you cannot use if-else declarations to the fork code, so use ternary declarations.

This ternary statement checks whether questionNumber is less than the length of the questions array, which basically checks if there are still questions to be answered. If there are, the question is displayed with two buttons, one for TRUE and one for FALSE. If there are no more questions, we display a GAME OVER message with the player’s score.

One thing to note when deriving view code using ternary operators is that you must use the html function for each new fork.

The buttons both have a line onclick an event listener attached to them that calls the same event handler, checkAnswer and accepts an argument either true Where false depending on which button you pressed. Now let’s write this event handler:

const checkAnswer = answer => event => Update(state => ({
    score: answer === state.questions[state.index].answer ? state.score + 1 : state.score,
    index: state.index + 1
}))

This event handler accepts an additional argument of answer as good as event object that all event handlers accept, so it must be curried using the double-arrow notation seen above. He calls the Update function that uses a ternary operator to check if the answer provided as an argument matches the answer to the current question, if so, then the score the property is increased by 1, otherwise the score remains the same. It also increases the value of the index property, so the following question will appear.

This event handler should now be added to the Statewith the View. We can do this using shorthand object notation, as long as checkAnswer and View are defined before State:

const State = {
  score: 0,
  index: 0,
  questions: [
    {question: "A Nanny State is a country where nannies are employed by the state", answer: false},
    {question: "Nanny State is also the name of a beer", answer: true},
    {question: "Mila Kunis and Ashton Kutcher employ 16 nannies to help raise their children", answer: false},
    {question: "The Nanny McPhee films are based on the Nurse Matilda books", answer: true},
    {question: "Nanny State uses the µhtml library for rendering", answer: true},
],
  checkAnswer,
  View
}

Now that it’s all part of the Stateall that remains is to define Update function by calling the Nanny function and provide State as an argument:

const Update = Nanny(State)

And that’s all! The quiz should start immediately and you should be able to answer each question and the score will change depending on whether the answer is correct or not. Another example of an interactive app that’s quick to build with a minimal amount of code. Try answering the questions and see how you do. You can see the finished code in the CodePen below:

True or false quiz

Once you’ve had fun playing and got used to how Nanny State works, here are some ideas for expanding the game:

  • Add more questions
  • Add “Start” and “Replay” buttons to allow players to replay
  • Randomly select questions
  • Keep track of the highest score
  • Add questions with other answers, except true or false
  • Create a multiple choice quiz

Nanny State includes many more other benefits such as built-in support for using local storage and routing. See the documentation for more information or contact us if you have any questions.

I would like to know what you think of Nanny State. Would you consider using it for a project? Is there anything else you would like to know about this? Leave a comment in our community!

Share.

About Author

Comments are closed.