Simple State Management in JavaScript with Nanny State

Share this article

Nanny State

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

In this post, I’m going to explain how Nanny State works and then demonstrate what it can do with a couple of examples.

Key Takeaways

  1. Introduction to Nanny State: Nanny State is a minimalistic library created for building state-based web applications using Vanilla JS, designed to be simpler and with less overhead compared to React. It leverages a single app-wide state object rather than individual states for each component, drawing inspiration from HyperApp and Elm, aiming for easy adoption without the need for learning new syntax.

  2. One-Way Data Flow Model: The library operates on a one-way data flow model consisting of three core parts: State (an object holding all app data), View (a function returning HTML based on the current state), and Update (the sole method for modifying state and re-rendering the view). This model emphasizes simplicity, with the state object serving as the single source of truth, ensuring deterministic, consistent, and predictable app behavior.

  3. Practical Examples and Extensibility: Through hands-on examples, including a basic “Hello Nanny State” app and a more complex True or False quiz game, the article demonstrates the ease and efficiency of building dynamic, reactive web apps with Nanny State. The library’s potential for creating interactive applications with minimal code is showcased, alongside suggestions for further extending the quiz game, highlighting Nanny State’s versatility and support for advanced features like local storage and routing.

One-Way Data Flow

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

  • State – an object that stores all the app data
  • View – a function that returns a string of HTML based on the current state
  • Update – a function that is the only way to change the state and re-render the view
One way data flow

In Nanny State, the state is everything. The state object is the single source of truth for your app – every bit of app data is a property of this object. Even event handlers used in the View are methods of the state object.

The View is a representation of the state as HTML. It changes whenever the state changes and allows users to interact with the app.

The Update function is the only way the state can be changed. It is a single entry-point for updating the state and ensures that changes are deterministic, consistent and predictable.

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

  1. What data do I need to store in my app? This will make up the properties of the State object
  2. How do I want the app data to be presented on the page? This will help you create the View function
  3. How will the 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 have a go at making something a bit 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>`

Nanny state uses µhtml to render HTML. The View function always accepts the state objects as its only parameter. It then uses the html function provided by µhtml to create HTML based on the template literal it is provided as an argument.

Using a template literal means that we can use the ${variable} notation to insert properties of the state into the view. In this example we are using it to insert the value of the name property inside the <h1> element.

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

The State object is where all the app data is stored. It includes any properties and values that will be displayed in the View and might change over the life-cycle of the app, such as the name property in this example.

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

const Update = Nanny(State)

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

See the Pen Nanny State – Hello Nanny State (SitePoint) by DAZ (@daz4126) on CodePen.

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 that they want to say hello to. Update the code so it looks like the following:

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’ve added an <input> element to the View. Event listeners are defined inline in the view, so in this example we have an oninput event listener attached to the <input> element. This will call the changeName event handler, which is a method of the state object, whenever any input is detected. This event listener needs defining, so let’s take a closer look at it:

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 it is called, we want to update the State object, so we use the Update function, as this is the only way we can update the State.

The argument we provide to the Update function is an object that contains any properties that we want to update in the 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 into 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 from the input field and instantly re-render the page. Using µhtml for rendering means that only the parts of the View that have actually changed get updated. This means that re-rendering after aState update is both efficient and blazingly fast.

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

See the Pen Nanny State – Hello Nanny State with Input (SitePoint) by DAZ (@daz4126) on CodePen.

Nanny State makes it super easy to write reactive state-based apps. As you can see, there’s not much code required to build a dynamic state-based application that reacts to user interaction. This is the beauty of Nanny State.

True or False Quiz

Now we’ve seen a basic example, let’s try making something a bit more complex. We’ll use Nanny State to build a True or False quiz game. Open up a new pen on CodePen and follow along.

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

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

Next, we’ll create the State object and fill it with the initial property values that 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 has answered correctly and starts at 0
  • index – this keeps track of which question the player is up to and corresponds to the last property which is the questions array.
  • questions – This is an array of objects with question and answer properties. The question property is a string and the answer property is a Boolean

Now we’ve created the data, let’s create the View to visualise that 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.index].question}</p>
     <button onclick=${state.checkAnswer(true)}>TRUE</button>
     <button onclick=${state.checkAnswer(false)}>FALSE</button>`
:
html`<h2>Game Over, you scored ${state.score}</h2>`
}`

This is a slightly more complicated View than we saw in the earlier example, but most of it should be fairly self-explanatory. After the title heading, we display the score using the score property of the State object. Then we use a ternary operator to fork the view. Because the view is written using template literals, you can’t use if-else statements to fork code, so need to use ternary statements.

This ternary statement checks if questionNumber is less than the length of the questions array, which is basically checking to see if there are still any questions left to answer. If there are then the question is displayed with two buttons, on for TRUE and one for FALSE. If there are no questions left then we show a GAME OVER message along with the player’s score.

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

The buttons both have an inline onclick event listener attached to them that calls the same event handler, checkAnswer and accepts an argument of either true or false depending on which button was pressed. Let’s write this event handler now:

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 extra argument of answer as well as the event object that all event handlers accept, so it needs to be curried using the double arrow notation seen above. It calls the Update function which uses a ternary operator to check if the answer provided as an argument matches the answer to the current question, if it does then the score property is increased by 1, if not then the score stays the same. It also increases the value of the index property, so the next question will be displayed.

This event handler now needs adding to the State, along with the View. We can do this using the object shorthand 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 everything is part of the State, all that is left to do is define the Update function by calling the Nanny function and providing State as an argument:

const Update = Nanny(State)

And that’s it! The quiz should start running straight away and you should be able to answer each question and the score will change based on if the answer is right or not. Another example of an interactive app that is quick to build with minimal amount of code. Have a go at answering the questions and see how you get on. You can see the finished code in the CodePen below:

See the Pen True or False Quiz – SitePoint by DAZ (@daz4126) on CodePen.

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

  • Add more questions
  • Add a ‘Start’ and ‘Play Again’ buttons to allow players to play again
  • Select questions at random
  • Keep track of the highest score
  • Add questions with other answers, except true or false
  • Create a multiple-choice quiz

Nanny State includes a lot more other goodies such as built-in support for using local storage and routing. See the docs for more info or get in touch if you have any questions.

I’d love to hear what you think of Nanny State. Would you consider using it for a project? Is there anything more you’d like to know about it? Leave a comment in our community!

Frequently Asked Questions on Simple State Management in JavaScript

What is the concept of state management in JavaScript?

State management in JavaScript refers to the process of tracking and controlling changes to the state of an application. The state of an application is a representation of all the data that changes over time and affects the behavior of the application. This includes user interactions, server responses, and system events. State management is crucial in JavaScript applications, especially in complex ones, as it helps maintain consistency, predictability, and control over the application’s behavior.

How does Nanny State simplify state management?

Nanny State is a simple state management library for JavaScript that simplifies state management by providing a straightforward and intuitive API. It allows developers to define states and transitions between states, and it automatically manages state changes and updates. This means developers can focus on writing the logic of their application, rather than worrying about managing state changes manually.

What are the benefits of using a state management system like Nanny State?

Using a state management system like Nanny State can greatly simplify the development process. It provides a structured way to manage state changes, which can make your code easier to understand and maintain. It also helps ensure consistency and predictability in your application, as it automatically manages state changes and updates. This can help prevent bugs and make your application more reliable.

How does state management differ in vanilla JavaScript and Nanny State?

In vanilla JavaScript, state management can be quite complex and manual. Developers need to manually track and control state changes, which can be error-prone and difficult to manage in large applications. On the other hand, Nanny State simplifies state management by providing a straightforward API for defining states and transitions, and it automatically manages state changes and updates.

Can I use Nanny State with other JavaScript frameworks?

Yes, Nanny State is a standalone library that can be used with any JavaScript framework or library. It does not have any dependencies, so it can be easily integrated into any JavaScript project.

How does Nanny State compare to other state management libraries?

Nanny State is designed to be simple and intuitive, making it a great choice for developers who want a straightforward solution for state management. While other libraries may offer more advanced features, they can also be more complex and difficult to use. Nanny State strikes a balance between simplicity and functionality, making it a versatile choice for many applications.

What are some common use cases for Nanny State?

Nanny State can be used in any JavaScript application that requires state management. This includes web applications, mobile apps, and even server-side applications. Some common use cases include managing user interactions, tracking server responses, and controlling system events.

How do I get started with Nanny State?

To get started with Nanny State, you can install it via npm or yarn. Once installed, you can import it into your project and start defining states and transitions. The Nanny State documentation provides a detailed guide on how to use the library.

Are there any limitations to using Nanny State?

Nanny State is designed to be simple and easy to use, but it may not have all the advanced features of more complex state management libraries. However, for many applications, its simplicity and ease of use make it a great choice.

Where can I find more resources on Nanny State and state management in JavaScript?

You can find more resources on Nanny State and state management in JavaScript on the Nanny State GitHub page, as well as on various online tutorials, blogs, and forums. The JavaScript community is very active and there are many resources available to help you learn more about state management.

Darren JonesDarren Jones
View Author

Darren loves building web apps and coding in JavaScript, Haskell and Ruby. He is the author of Learn to Code using JavaScript, JavaScript: Novice to Ninja and Jump Start Sinatra.He is also the creator of Nanny State, a tiny alternative to React. He can be found on Twitter @daz4126.

Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week
Loading form