Build a state management system with vanilla JavaScript

Managing state is not a new thing in software, but it’s still relatively new for building software in JavaScript. Traditionally, we’d keep state within the DOM itself or even assign it to a global object in the window. Now though, we’re spoiled with choices for libraries and frameworks to help us with this. Libraries like Redux, MobX and Vuex make managing cross-component state almost trivial. This is great for an application’s resilience and it works really well with a state-first, reactive framework such as React or Vue.

How do these libraries work though? What would it take to write one ourselves? Turns out, it’s pretty straightforward and there’s an opportunity to learn some really common patterns and also learn about some useful modern APIs that are available to us.

Before we get started, it’s recommended that you have an intermediary knowledge of JavaScript. You should know about data types and ideally, you should have a grasp of some more modern ES6+ JavaScript features. If not, we’ve got your back. It’s also worth noting that I’m not saying that you should replace Redux or MobX with this. We’re working on a little project to skill-up together and, hey, it could definitely power a small application if you were keeping an eye on the size of your JavaScript payload.

Getting started

Before we dive into code, take a look at what we’re building. It’s a “done list” that adds up the things you’ve achieved today. It’ll update various elements of the UI like magic — all with no framework dependencies. That’s not the real magic though. Behind the scenes, we’ve got a little state system that’s sitting, waiting for instructions and maintaining a single source of truth in a predictable fashion.

View Demo

View Repo

Pretty cool, right? Let’s do some admin first. I’ve put together a bit of a boilerplate so we can keep this tutorial snappy. The first thing you need to do is either clone it from GitHub, or download a ZIP archive and expand it.

Now that you’ve got that going, you’re going to need to run it in a local web server. I like to use a package called http-server for these sort of things, but you can use whatever you want. When you’ve got it running locally, you should see something that looks like this:

The initial state of our boilerplate.

Setting up our structure

Open the root folder in your favorite text editor. This time, for me, the root folder is:

~/Documents/Projects/vanilla-js-state-management-boilerplate/

You should see a structure that looks a bit like this:

/src
├── .eslintrc
├── .gitignore
├── LICENSE
└── README.md

Pub/Sub

Next, open up the src folder and then open up the js folder that lives in there. Make a new folder called lib. Inside that, make a new file called pubsub.js.

The structure of your js directory should look like this:

/js
├── lib
└── pubsub.js

Open up pubsub.js because we’re going to make a little Pub/Sub pattern, which is short for “Publish/Subscribe.” We’re creating the functionality that allows other parts of our application to subscribe to named events. Another part of the application can then publish those events, often with some sort of relevant payload.

Pub/Sub is sometimes hard to grasp, so how about an analogy? Imagine you work in a restaurant and your customers have a starter and a main course. If you’ve ever worked in a kitchen, you’ll know that when the server clears the starters, they let the chefs know which table’s starters are cleared. This is a cue to start on the main courses for that table. In a big kitchen, there are a few chefs who will probably be on different dishes. They’re all subscribed to the cue from the server that the customers have finished their starters, so they know to do their function, which is to prepare the main course. So, you’ve got multiple chefs waiting on the same cue (named event) to do different functions (callback) to each other.

Hopefully thinking of it like that helps it make sense. Let’s move on!

The PubSub pattern loops through all of the subscriptions and fires their callbacks with that payload. It’s a great way of creating a pretty elegant reactive flow for your app and we can do it with only a few lines of code.

Add the following to pubsub.js:

export default class PubSub { constructor() { this.events = {}; }
}

What we’ve got there is a fresh new class and we’re setting this.events as a blank object by default. The this.events object will hold our named events.

After the constructor’s closing bracket, add the following:

subscribe(event, callback) { let self = this; if(!self.events.hasOwnProperty(event)) { self.events[event] = []; } return self.events[event].push(callback);
}

This is our subscribe method. You pass a string event, which is the event’s unique name and a callback function. If there’s not already a matching event in our events collection, we create it with a blank array so we don’t have to type check it later. Then, we push the callback into that collection. If it already existed, this is all the method would do. We return the length of the events collection, because it might be handy for someone to know how many events exist.

Now that we’ve got our subscribe method, guess what comes next? You know it: the publish method. Add the following after your subscribe method:

publish(event, data = {}) { let self = this; if(!self.events.hasOwnProperty(event)) { return []; } return self.events[event].map(callback => callback(data));
}

This method first checks to see if the passed event exists in our collection. If not, we return an empty array. No dramas. If there is an event, we loop through each stored callback and pass the data into it. If there are no callbacks (which shouldn’t ever be the case), it’s all good, because we created that event with an empty array in the subscribe method.

That’s it for PubSub. Let’s move on to the next part!

The core Store object

Now that we’ve got our Pub/Sub module, we’ve got our only dependency for the meat‘n’taters of this little application: the Store. We’ll go ahead and start fleshing that out now.

Let’s first outline what this does.

The Store is our central object. Each time you see @import store from '../lib/store.js, you’ll be pulling in the object that we’re going to write. It’ll contain a state object that, in turn, contains our application state, a commit method that will call our >mutations, and lastly, a dispatch function that will call our actions. Amongst this and core to the Store object, there will be a Proxy-based system that will monitor and broadcast state changes with our PubSub module.

Start off by creating a new directory in your js directory called store. In there, create a new file called store.js. Your js directory should now look like this:

/js
└── lib └── pubsub.js
└──store └── store.js

Open up store.js and import our Pub/Sub module. To do that, add the following right at the top of the file:

import PubSub from '../lib/pubsub.js';

For those who work with ES6 regularly, this will be very recognizable. Running this sort of code without a bundler will probably be less recognizable though. There’s a heck of a lot of support already for this approach, too!

Next, let’s start building out our object. Straight after the import, add the following to store.js:

export default class Store { constructor(params) { let self = this; }
}

This is all pretty self-explanatory, so let’s add the next bit. We’re going to add default objects for state, actions, and mutations. We’re also adding a status element that we’ll use to determine what the object is doing at any given time. This goes right after let self = this;:

self.actions = {};
self.mutations = {};
self.state = {};
self.status = 'resting';

Straight after that, we’ll create a new PubSub instance that will be attached the Store as an events element:

self.events = new PubSub();

Next, we’re going to search the passed params object to see if any actions or mutations were passed in. When the Store object is instantiated, we can pass in an object of data. Included in that can be a collection of actions and mutations that control the flow of data in our store. The following code comes next right after the last line that you added:

if(params.hasOwnProperty('actions')) { self.actions = params.actions;
} if(params.hasOwnProperty('mutations')) { self.mutations = params.mutations;
}

That’s all of our defaults set and nearly all of our potential params set. Let’s take a look at how our Store object keeps track of all of the changes. We’re going to use a Proxy to do this. What the Proxy does is essentially work on behalf of our state object. If we add a get trap, we can monitor every time that the object is asked for data. Similarly with a set trap, we can keep an eye on changes that are made to the object. This is the main part we’re interested in today. Add the following straight after the last lines that you added and we’ll discuss what it’s doing:

self.state = new Proxy((params.state || {}), { set: function(state, key, value) { state[key] = value; console.log(`stateChange: ${key}: ${value}`); self.events.publish('stateChange', self.state); if(self.status !== 'mutation') { console.warn(`You should use a mutation to set ${key}`); } self.status = 'resting'; return true; }
});

What’s happening here is we’re trapping the state object set operations. That means that when a mutation runs something like state.name = 'Foo' , this trap catches it before it can be set and provides us an opportunity to work with the change or even reject it completely. In our context though, we’re setting the change and then logging it to the console. We’re then publishing a stateChange event with our PubSub module. Anything subscribed to that event’s callback will be called. Lastly, we’re checking the status of Store. If it’s not currently running a mutation, it probably means that the state was updated manually. We add a little warning in the console for that to give the developer a little telling off.

There’s a lot going on there, but I hope you’re starting to see how this is all coming together and importantly, how we’re able to maintain state centrally, thanks to Proxy and Pub/Sub.

Dispatch and commit

Now that we’ve added our core elements of the Store, let’s add two methods. One that will call our actions named dispatch and another that will call our mutations called commit. Let’s start with dispatch by adding this method after your constructor in store.js:

dispatch(actionKey, payload) { let self = this; if(typeof self.actions[actionKey] !== 'function') { console.error(`Action "${actionKey} doesn't exist.`); return false; } console.groupCollapsed(`ACTION: ${actionKey}`); self.status = 'action'; self.actions[actionKey](self, payload); console.groupEnd(); return true;
}

The process here is: look for an action and, if it exists, set a status and call the action while creating a logging group that keeps all of our logs nice and neat. Anything that is logged (like a mutation or Proxy log) will be kept in the group that we define. If no action is set, it’ll log an error and bail. That was pretty straightforward, and the commit method is even more straightforward.

Add this after your dispatch method:

commit(mutationKey, payload) { let self = this; if(typeof self.mutations[mutationKey] !== 'function') { console.log(`Mutation "${mutationKey}" doesn't exist`); return false; } self.status = 'mutation'; let newState = self.mutations[mutationKey](self.state, payload); self.state = Object.assign(self.state, newState); return true;
}

This method is pretty similar, but let’s run through the process anyway. If the mutation can be found, we run it and get our new state from its return value. We then take that new state and merge it with our existing state to create an up-to-date version of our state.

With those methods added, our Store object is pretty much complete. You could actually modular-ize this application now if you wanted because we’ve added most of the bits that we need. You could also add some tests to check that everything run as expected. But I’m not going to leave you hanging like that. Let’s make it all actually do what we set out to do and continue with our little app!

Creating a base component

To communicate with our store, we’ve got three main areas that update independently based on what’s stored in it. We’re going to make a list of submitted items, a visual count of those items, and another one that’s visually hidden with more accurate information for screen readers. These all do different things, but they would all benefit from something shared to control their local state. We’re going to make a base component class!

First up, let’s create a file. In the lib directory, go ahead and create a file called component.js. The path for me is:

~/Documents/Projects/vanilla-js-state-management-boilerplate/src/js/lib/component.js

Once that file is created, open it and add the following:

import Store from '../store/store.js'; export default class Component { constructor(props = {}) { let self = this; this.render = this.render || function() {}; if(props.store instanceof Store) { props.store.events.subscribe('stateChange', () => self.render()); } if(props.hasOwnProperty('element')) { this.element = props.element; } }
}

Let’s talk through this chunk of code. First up, we’re importing the Store class. This isn’t because we want an instance of it, but more for checking one of our properties in the constructor. Speaking of which, in the constructor we’re looking to see if we’ve got a render method. If this Component class is the parent of another class, then that will have likely set its own method for render. If there is no method set, we create an empty method that will prevent things from breaking.

After this, we do the check against the Store class like I mentioned above. We do this to make sure that the store prop is a Store class instance so we can confidently use its methods and properties. Speaking of which, we’re subscribing to the global stateChange event so our object can react. This is calling the render function each time the state changes.

That’s all we need to write for that class. It’ll be used as a parent class that other components classes will extend. Let’s crack on with those!

Creating our components

Like I said earlier, we’ve got three components to make and their all going to extend the base Component class. Let’s start off with the biggest one: the list of items!

In your js directory, create a new folder called components and in there create a new file called list.js. For me the path is:

~/Documents/Projects/vanilla-js-state-management-boilerplate/src/js/component/list.js

Open up that file and paste this whole chunk of code in there:

import Component from '../lib/component.js';
import store from '../store/index.js'; export default class List extends Component { constructor() { super({ store, element: document.querySelector('.js-items') }); } render() { let self = this; if(store.state.items.length === 0) { self.element.innerHTML = `<p class="no-items">You've done nothing yet &#x1f622;</p>`; return; } self.element.innerHTML = ` <ul class="app__items"> ${store.state.items.map(item => { return ` <li>${item}<button aria-label="Delete this item">×</button></li> ` }).join('')} </ul> `; self.element.querySelectorAll('button').forEach((button, index) => { button.addEventListener('click', () => { store.dispatch('clearItem', { index }); }); }); }
};

I hope that code is pretty self-explanatory after what we’ve learned earlier in this tutorial, but let’s skim through it anyway. We start off by passing our Store instance up to the Component parent class that we are extending. This is the Component class that we’ve just written.

After that, we declare our render method that gets called each time the stateChange Pub/Sub event happens. In this render method we put out either a list of items, or a little notice if there are no items. You’ll also notice that each button has an event attached to it and they dispatch and action within our store. This action doesn’t exist yet, but we’ll get to it soon.

Next up, create two more files. These are two new components, but they’re tiny — so we’re just going to paste some code in them and move on.

First, create count.js in your component directory and paste the following in it:

import Component from '../lib/component.js';
import store from '../store/index.js'; export default class Count extends Component { constructor() { super({ store, element: document.querySelector('.js-count') }); } render() { let suffix = store.state.items.length !== 1 ? 's' : ''; let emoji = store.state.items.length > 0 ? '&#x1f64c;' : '&#x1f622;'; this.element.innerHTML = ` <small>You've done</small> ${store.state.items.length} <small>thing${suffix} today ${emoji}</small> `; }
}

Looks pretty similar to list, huh? There’s nothing in here that we haven’t already covered, so let’s add another file. In the same components directory add a status.js file and paste the following in it:

import Component from '../lib/component.js';
import store from '../store/index.js'; export default class Status extends Component { constructor() { super({ store, element: document.querySelector('.js-status') }); } render() { let self = this; let suffix = store.state.items.length !== 1 ? 's' : ''; self.element.innerHTML = `${store.state.items.length} item${suffix}`; }
}

Again, we’ve covered everything in there, but you can see how handy it is having a base Component to work with, right? That’s one of the many benefits of Object-orientated Programming, which is what most of this tutorial is based on.

Finally, let’s check that your js directory is looking right. This is the structure of where we’re currently at:

/src
├── js
│ ├── components
│ │ ├── count.js
│ │ ├── list.js
│ │ └── status.js
│ ├──lib
│ │ ├──component.js
│ │ └──pubsub.js
└───── store └──store.js └──main.js

Let’s wire it up

Now that we’ve got our front-end components and our main Store, all we’ve got to do is wire it all up.

We’ve got our store system and the components to render and interact with its data. Let’s now wrap up by hooking up the two separate ends of the app and make the whole thing work together. We’ll need to add an initial state, some actions and some mutations. In your store directory, add a new file called state.js. For me it’s like this:

~/Documents/Projects/vanilla-js-state-management-boilerplate/src/js/store/state.js

Open up that file and add the following:

export default { items: [ 'I made this', 'Another thing' ]
};

This is pretty self-explanatory. We’re adding a default set of items so that on first-load, our little app will be fully interactive. Let’s move on to some actions. In your store directory, create a new file called actions.js and add the following to it:

export default { addItem(context, payload) { context.commit('addItem', payload); }, clearItem(context, payload) { context.commit('clearItem', payload); }
};

The actions in this app are pretty minimal. Essentially, each action is passing a payload to a mutation, which in turn, commits the data to store. The context, as we learned earlier, is the instance of the Store class and the payload is passed in by whatever dispatches the action. Speaking of mutations, let’s add some. In this same directory add a new file called mutations.js. Open it up and add the following:

export default { addItem(state, payload) { state.items.push(payload); return state; }, clearItem(state, payload) { state.items.splice(payload.index, 1); return state; }
};

Like the actions, these mutations are minimal. In my opinion, your mutations should always be simple because they have one job: mutate the store’s state. As a result, these examples are as complex as they should ever be. Any proper logic should happen in your actions. As you can see for this system, we return the new version of the state so that the Store`'s <code>commit method can do its magic and update everything. With that, the main elements of the store system are in place. Let’s glue them together with an index file.

In the same directory, create a new file called index.js. Open it up and add the following:

import actions from './actions.js';
import mutations from './mutations.js';
import state from './state.js';
import Store from './store.js'; export default new Store({ actions, mutations, state
});

All this file is doing is importing all of our store pieces and glueing them all together as one succinct Store instance. Job done!

The final piece of the puzzle

The last thing we need to put together is the main.js file that we included in our index.html page waaaay up at the start of this tutorial. Once we get this sorted, we’ll be able to fire up our browsers and enjoy our hard work! Create a new file called main.js at the root of your js directory. This is how it looks for me:

~/Documents/Projects/vanilla-js-state-management-boilerplate/src/js/main.js

Open it up and add the following:

import store from './store/index.js'; import Count from './components/count.js';
import List from './components/list.js';
import Status from './components/status.js'; const formElement = document.querySelector('.js-form');
const inputElement = document.querySelector('#new-item-field');

So far, all we’re doing is pulling in dependencies that we need. We’ve got our Store, our front-end components and a couple of DOM elements to work with. Let’s add this next bit to make the form interactive, straight under that code:

formElement.addEventListener('submit', evt => { evt.preventDefault(); let value = inputElement.value.trim(); if(value.length) { store.dispatch('addItem', value); inputElement.value = ''; inputElement.focus(); }
});

What we’re doing here is adding an event listener to the form and preventing it from submitting. We then grab the value of the textbox and trim any whitespace off it. We do this because we want to check if there’s actually any content to pass to the store next. Finally, if there’s content, we dispatch our addItem action with that content and let our shiny new store deal with it for us.

Let’s add some more code to main.js. Under the event listener, add the following:

const countInstance = new Count();
const listInstance = new List();
const statusInstance = new Status(); countInstance.render();
listInstance.render();
statusInstance.render();

All we’re doing here is creating new instances of our components and calling each of their render methods so that we get our initial state on the page.

With that final addition, we are done!

Open up your browser, refresh and bask in the glory of your new state managed app. Go ahead and add something like “Finished this awesome tutorial” in there. Pretty neat, huh?

Next steps

There’s a lot of stuff you could do with this little system that we’ve put together. Here are some ideas for taking it further on your own:

  • You could implement some local storage to maintain state, even when you reload
  • You could pull out the front-end of this and have a little state system for your projects
  • You could continue to develop the front-end of this app and make it look awesome. (I’d be really interested to see your work, so please share!)
  • You could work with some remote data and maybe even an API
  • You could take what you’ve learned about Proxy and the Pub/Sub pattern and develop those transferable skills further

Wrapping up

Thanks for learning about how these state systems work with me. The big, popular ones are much more complex and smarter that what we’ve done — but it’s still useful to get an idea of how these systems work and unravel the mystery behind them. It’s also useful to learn how powerful JavaScript can be with no frameworks whatsoever.

If you want a finished version of this little system, check out this GitHub repository. You can also see a demo here.

If you develop on this further, I’d love to see it, so hit me up on Twitter or post in the comments below if you do!

The post Build a state management system with vanilla JavaScript appeared first on CSS-Tricks.

Finite State Machines with React

As JavaScript applications on the web have grown more complex, so too has the complexity of dealing with state in those applications — state being the aggregate of all the data that an application needs to perform its function. Over the last several years, there has been a ton of great innovation in the realm of state management through tools like Redux, MobX, and Vuex. Something that hasn’t gotten quite as much attention, though, is state design.

What in the heck do I mean by state design?

Let’s set the scene a little bit. In the past, when building an application that needs to fetch some data from a backend service and display it to the user, I’ve designed my state to use boolean flags for various things like isLoading, isSuccess, isError, and so on down the line. As this number of boolean flags grows, though, the number of possible states that my application can have grows exponentially with it — significantly increasing the likelihood of a user encountering an unintentional or error state.

To solve this issue, I’ve spent the last several months exploring the use of finite state machines as a way to better design the state of my applications.

Finite state machines are a mathematical model of computation, initially developed in the early 1940s, that have been used for decades to build both hardware and software for a wide array of technologies.

A finite state machine can be defined as any abstract machine that exists in exactly one of a finite number of states at a given time. In more practical terms, though, a state machine is characterized by a list of states with each state defining a finite, deterministic set of states that can be transitioned to by a given action.

Due to this finite and deterministic nature, we can use state diagrams to visualize our application — before or after it’s been built.

For example, if we wanted to visualize an authentication workflow, we could have three overarching states that our application could be in for a user: logged in, logged out, or loading.

A state diagram showing an application going from logged out, to loading, to logged in.

State machines, owing to their predictability, are especially popular in applications where reliability is critical — such as aviation software, manufacturing, and even the NASA Space Launch System. They’ve been a mainstay in the game development community for decades, as well.

In this article, we’ll tackle building something that most applications on the web use: authentication. We’ll use the state diagram above to guide us.

Before we get started, though, let’s familiarize ourselves with some of the libraries and APIs we’ll be using to build this application.

React Context API

React 16.3 introduced a new, stable version of the Context API. If you’ve worked much with React in the past, you may be familiar with how data is passed from parent to child through props. When you have certain data that is needed by a variety of components, you can end up doing what’s known as prop drilling — passing data through multiple levels of the component tree to get the data to a component that needs it.

Context helps alleviate the pain of prop drilling by providing a way to share data between components without having to explicitly pass that data through the component tree, making it perfect for storing authentication data.

When we create context, we get a Provider and Consumer pair. The provider will act as the “smart,” stateful component that contains our state machine definition, and maintains a record of the current state of our application.

xstate

xstate is a JavaScript library for functional, stateless finite state machines and statecharts — it will provide us a nice, clean API for managing definitions and transitions through our states.

A stateless finite state machine library might sound a bit strange, but essentially what it means is that xstate only cares about the state and transition that you pass it — meaning it’s up to your application to keep track of its own current state.

xstate has a lot of features worth mentioning that we won’t cover much in this article (since we’ll only begin to scratch the surface on statecharts): hierarchical machines, parallel machines, history states, and guards, just to name a few.

The approach

So, now that we’ve had a little bit of an introduction to both Context and xstate, let’s talk about the approach we’ll be taking.

We’ll start by defining the context for our application, then creating a stateful <App /> component (our provider) that will contain our authentication state machine, along with information about the current user and a method for the user to logout.

To set the stage a bit, let’s take a quick look at a CodePen demo of what we’ll be building.

See the Pen Authentication state machine example by Jon Bellah (@jonbellah) on CodePen.

So, without further ado, let’s dig into some code!

Defining our context

The first thing we need to do is define our application context and set it up with some default values. Default values in context are helpful for allowing us to test components in isolation, since the default values are only used if there is no matching provider.

For our application, we’re going to set up a few defaults: authState which will be the authentication state of the current user, an object called user which will contain data about our user if they’re authenticated, then a logout() method that can be called anywhere in the app if the user is authenticated.

const Auth = React.createContext({ authState: 'login', logout: () => {}, user: {},
});

Defining our machine

When we think about how authentication behaves in an application, in its simplest form, there are three primary states: logged out, logged in, and loading. These are the three states we diagramed earlier.

Looking back at that state diagram, our machine consists of those same three states: logged out, logged in, and loading. We also have four different action types that can be fired: SUBMIT, SUCCESS, FAIL, and LOGOUT.

We can model that behavior in code like so:

const appMachine = Machine({ initial: 'loggedOut', states: { loggedOut: { onEntry: ['error'], on: { SUBMIT: 'loading', }, }, loading: { on: { SUCCESS: 'loggedIn', FAIL: 'loggedOut', }, }, loggedIn: { onEntry: ['setUser'], onExit: ['unsetUser'], on: { LOGOUT: 'loggedOut', }, }, },
});

So, we just expressed the diagram from earlier in code, but are you ready for me to let you in on a little secret? That diagram was generated from this code using David Khourshid’s xviz library — which can be used to visually explore the actual code that powers your state machines.

If you’re interested in diving deeper into complex user interfaces using finite state machines, David Khourshid has a related article here on CSS-Tricks worth checking out.

This can be an incredibly powerful tool when trying to debug problematic states in your application.

Referring back to the code above now, we define our initial application state — which we’re calling loggedOut since we’ll want to show the login screen on an initial visit.

Note that in a typical application, you’d probably want to start from the loading state and determine if the user was previously authenticated… but since we’re faking the login process, we’re starting from the logged out state.

In the states object, we define each of our states along with the corresponding actions and transitions for each of those states. Then we pass all that as an object to the Machine() function, which is imported from xstate.

Along with our loggedOut and loggedIn states, we’ve defined some actions that we want to fire when our application enters or exits those states. We’ll look at what those actions do in a bit.

This is our state machine.

To break things down one more time, let’s look at the loggedOut: { on: { SUBMIT: 'loading'} } line . This means that if our application is in the loggedOut state and we call our transition function with an action of SUBMIT, our application will always transition from the loggedOut state to the loading state. We can make that transition by calling appMachine.transition('loggedOut', 'SUBMIT').

From there, the loading state will either move the user along as an authenticated user or send them back to the login screen and display an error message.

Creating our context provider

The context provider will be the component that sits at the top level of our application and houses all the data related to an authenticated — or unauthenticated — user.

Working in the same file as our state machine definition, let’s create an <App /> component and set it up with everything we’ll need. Don’t worry, we’ll cover what each method does in just a moment.

class App extends React.Component { constructor(props) { super(props); this.state = { authState: appMachine.initialState.value, error: '', logout: e => this.logout(e), user: {}, }; } transition(event) { const nextAuthState = appMachine.transition(this.state.authState, event.type); const nextState = nextAuthState.actions.reduce( (state, action) => this.command(action, event) || state, undefined, ); this.setState({ authState: nextAuthState.value, ...nextState, }); } command(action, event) { switch (action) { case 'setUser': if (event.username) { return { user: { name: event.username } }; } break; case 'unsetUser': return { user: {}, }; case 'error': if (event.error) { return { error: event.error, }; } break; default: break; } } logout(e) { e.preventDefault(); this.transition({ type: 'LOGOUT' }); } render() { return ( <Auth.Provider value={this.state}> <div className="w5"> <div className="mb2">{this.state.error}</div> {this.state.authState === 'loggedIn' ? ( <Dashboard /> ) : ( <Login transition={event => this.transition(event)} /> )} </div> </Auth.Provider> ); }
}

Whew, that was a lot of code! Let’s break it down into manageable chunks by taking a look at each method of this class individually.

In the constructor(), we’re setting our component state to the initial state of our appMachine, as well as setting our logout function in state, so that it can be passed through our application context to any consumer that needs it.

In the transition() method, we’re doing a few important things. First, we’re passing our current application state and the event type or action to xstate, so we can determine our next state. Then, in nextState, we take any actions associated with that next state (which will be one of our onEntry or onExit actions) and run them through the command() method — then we take all of the results and set our new application state.

In the command() method, we have a switch statement that returns an object — depending on the action type — which we use to pass data into our application state. This way, once a user has authenticated, we can set relevant details about that user — username, email, id, etc. — into our context, making it available to any of our consumer components.

Finally, in our render() method, we’re actually defining our provider component and then passing all of our current state through the value props, which makes the state available to all of the components beneath it in the component tree. Then, depending on the state of our application, we’re rendering either the dashboard or the login form for the user.

In this case, we have a pretty flat component tree beneath our provider (Auth.Provider), but remember that context allows that value to be available to any component beneath our provider in the component tree, regardless of depth. So, for example, if we have a component nested three or four levels down and we want to display the current user name, we can just grab that out of context, rather than drilling it all the way down to that one component.

Creating context consumers

Now, let’s create some components that consume our application context. From these components, we can do all sorts of things.

We can start by building a login component for our application.

class Login extends Component { constructor(props) { super(props); this.state = { yourName: '', } this.handleInput = this.handleInput.bind(this); } handleInput(e) { this.setState({ yourName: e.target.value, }); } login(e) { e.preventDefault(); this.props.transition({ type: 'SUBMIT' }); setTimeout(() => { if (this.state.yourName) { return this.props.transition({ type: 'SUCCESS', username: this.state.yourName, }, () => { this.setState({ username: '' }); }); } return this.props.transition({ type: 'FAIL', error: 'Uh oh, you must enter your name!', }); }, 2000); } render() { return ( <Auth.Consumer> {({ authState }) => ( <form onSubmit={e => this.login(e)}> <label htmlFor="yourName"> <span>Your name</span> <input id="yourName" name="yourName" type="text" value={this.state.yourName} onChange={this.handleInput} /> </label> <input type="submit" value={authState === 'loading' ? 'Logging in...' : 'Login' } disabled={authState === 'loading' ? true : false} /> </form> )} </Auth.Consumer> ); }
}

Oh my! That was another big chunk of code, so let’s walk through each method again.

In the constructor(), we’re declaring our default state and binding the handleInput() method so that it references the proper this internally.

In handleInput(), we’re taking the value of our form field from our render() method and setting that value in state — this is referred to as a controlled form.

The login() method is where you would normally place your authentication logic. In the case of this app, we’re just faking a delay with setTimeout() and either authenticating the user — if they’ve provided a name — or returning an error if the field was left empty. Note that the transition() function that it calls is actually the one we defined in our <App /> component, which has been passed down via props.

Finally, our render() method displays our login form, but notice that the <Login /> component is also a context consumer. We’re using the authState context to determine whether or not to show our login button in a disabled, loading state.

Using context from deep in the component tree

Now that we’ve handled the creation of our state machine and a way for users to login to our application, we can now rely on having information about that user within any component nested under our <Dashboard /> component — since it will only ever be rendered if the user is logged in.

So let’s create a stateless component that grabs the username of the current authenticated user and displays a welcome message. Since we’re passing the logout() method to all of our consumers, we can also give the user the option of logging out from anywhere in the component tree.

const Dashboard = () => ( <Auth.Consumer> {({ user, logout }) => ( <div> <div>Hello {user.name}</div> <button onClick={e => logout(e)}> Logout </button> </div> )} </Auth.Consumer>
);

Building larger applications with statecharts

Using finite state machines with React doesn’t have to be limited to authentication, nor does it have to be limited to the context API.

Using statecharts, you can have hierarchical machines and/or parallel machines — meaning individual React components can have their own internal state machine, but still be connected to the overall state of your application.

In this article, we’ve primarily focused on using xstate directly with the native Context API; in larger applications, I highly recommend looking at react-automata, which provides a thin layer of abstraction over the top of xstate. react-automata has the added benefit of being able to automagically generate Jest tests for your components.

State machines and state management tools are not mutually exclusive

It’s easy to get confused by thinking you must either use, say, xstate or Redux; but it’s important to note that state machines are more of an implementation concept, concerned with how you design your state — not necessarily how you manage it.

In fact, state machines can be used with just about any un-opinionated state management tool. I encourage you to explore various approaches to determine what works best for you, your team, and your application(s).

In conclusion

These concepts can be extended to the real world without having to refactor your entire application. State machines are an excellent refactor target — meaning, next time you’re working on a component that is littered with boolean flags like isFetching and isError, consider refactoring that component to use a state machine.

As a front-end developer, I’ve found that I’m often fixing one of two categories of bugs: display-related issues or unexpected application states.

State machines make the second category virtually disappear.

If you’re interested in diving deeper into state machines, I’ve spent the last several months working on a course on finite state machines — if you sign up for the email list, you’ll receive a discount code when the course launches in August.

The post Finite State Machines with React appeared first on CSS-Tricks.

Managing State in React With Unstated

As your application becomes more complex, the management of state can become tedious. A component’s state is meant to be self-contained, which makes sharing state across multiple components a headache. Redux is usually the go-to library to manage state in React, however, depending on how complex your application is, you might not need Redux.

Unstated is an alternative that provides you with the functionality to manage state across multiple components with a Container class and Provider and Subscribe components. Let’s see Unstated in action by creating a simple counter and then look at a more advanced to-do application.

Using Unstated to Create a Counter

The code for the counter we’re making is available on GitHub:

View Repo

You can add Unstated to your application with Yarn:

yarn add unstated

Container

The container extends Unstated’s Container class. It is to be used only for state management. This is where the initial state will be initialized and the call to setState() will happen.

import { Container } from 'unstated' class CounterContainer extends Container { state = { count: 0 } increment = () => { this.setState({ count: this.state.count + 1 }) } decrement = () => { this.setState({ count: this.state.count - 1 }) }
} export default CounterContainer

So far, we’ve defined the Container (CounterContainer), set its starting state for count at the number zero and defined methods for adding and subtracting to the component’s state in increments and decrements of one.

You might be wondering why we haven’t imported React at this point. There is no need to import it into the Container since we will not be rendering JSX at all.

Events emitters will be used in order to call setState() and cause the components to re-render. The components that will make use of this container will have to subscribe to it.

Subscribe

The Subscribe component is used to plug the state into the components that need it. From here, we will be able to call the increment and decrement methods, which will update the state of the application and cause the subscribed component to re-render with the correct count. These methods will be triggered by a couple of buttons that contain events listeners to add or subtract to the count, respectively.

import React from 'react'
import { Subscribe } from 'unstated' import CounterContainer from './containers/counter' const Counter = () => { return ( <Subscribe to={[CounterContainer]}> {counterContainer => ( <div> <div> // The current count value Count: { counterContainer.state.count } </div> // This button will add to the count <button onClick={counterContainer.increment}>Increment</button> // This button will subtract from the count <button onClick={counterContainer.decrement}>Decrement</button> </div> )} </Subscribe> )
} export default Counter

The Subscribe component is given the CounterContainer in the form of an array to its to prop. This means that the Subscribe component can subscribe to more than one container, and all of the containers are passed to the to prop of the Subscribe component in an array.

The counterContainer is a function that receives an instance of each container the Subscribe component subscribes to.

With that, we can now access the state and the methods made available in the container.

Provider

We’ll make use of the Provider component to store the container instances and allow the children to subscribe to it.

import React, { Component } from 'react';
import { Provider } from 'unstated' import Counter from './Counter' class App extends Component { render() { return ( <Provider> <Counter /> </Provider> ); }
} export default App;

With this, the Counter component can make use of our counterContainer.

Unstated allows you to make use of all the functionality that React’s setState() provides. For example, if we want to increment the previous state by one three times with one click, we can pass a function to setState() like this:

incrementBy3 = () => { this.setState((prevState) => ({ count: prevState.count + 1 })) this.setState((prevState) => ({ count: prevState.count + 1 })) this.setState((prevState) => ({ count: prevState.count + 1 }))
}

The idea is that the setState() still works like it does, but this time with the ability to keep the state contained in a Container class. It becomes easy to spread the state to only the components that need it.

Let’s Make a To-Do Application!

This is a slightly more advanced use of Unstated. Two components will subscribe to the container, which will manage all of the state, and the methods for updating the state. Again, the code is available on Github:

View Repo

The container will look like this:

import { Container } from 'unstated' class TodoContainer extends Container { state = { todos: [ 'Mess around with unstated', 'Start dance class' ], todo: '' }; handleDeleteTodo = (todo) => { this.setState({ todos: this.state.todos.filter(c => c !== todo) }) } handleInputChange = (event) => { const todo = event.target.value this.setState({ todo }); }; handleAddTodo = (event) => { event.preventDefault() this.setState(({todos}) => ({ todos: todos.concat(this.state.todo) })) this.setState({ todo: '' }); } } export default TodoContainer

The container has an initial todos state which is an array with two items in it. To add to-do items, we have a todo state set to an empty string.

We’re going to need a CreateTodo component that will subscribe to the container. Each time a value is entered, the onChange event will trigger then fire the handleInputChange() method we have in the container. Clicking the submit button will trigger handleAddTodo(). The handleDeleteTodo() method receives a to-do and filters out the to-do that matches the one passed to it.

import React from 'react'
import { Subscribe } from 'unstated' import TodoContainer from './containers/todoContainer' const CreateTodo = () => { return ( <div> <Subscribe to={[TodoContainer]}> {todos => <div> <form onSubmit={todos.handleAddTodo}> <input type="text" value={todos.state.todo} onChange={todos.handleInputChange} /> <button>Submit</button> </form> </div> } </Subscribe> </div> );
} export default CreateTodo

When a new to-do is added, the todos state made available in the container is updated. The list of todos is pulled from the container to the Todos component, by subscribing the component to the container.

import React from 'react';
import { Subscribe } from 'unstated'; import TodoContainer from './containers/todoContainer' const Todos = () => ( <ul> <Subscribe to={[TodoContainer]}> {todos => todos.state.todos.map(todo => ( <li key={todo}> {todo} <button onClick={() => todos.handleDeleteTodo(todo)}>X</button> </li> )) } </Subscribe> </ul>
); export default Todos

This component loops through the array of to-dos available in the container and renders them in a list.

Finally, we need to wrap the components that subscribe to the container in a provider like we did in the case of the counter. We do this in our App.js file exactly like we did in the counter example:

import React, { Component } from 'react';
import { Provider } from 'unstated' import CreateTodo from './CreateTodo'
import Todos from './Todos' class App extends Component { render() { return ( <Provider> <CreateTodo /> <Todos /> </Provider> ); }
} export default App;

Wrapping Up

There are different ways of managing state in React depending on the complexity of your application and Unstated is a handy library that can make it easier. It’s worth reiterating the point that Redux, while awesome, is not always the best tool for the job, even though we often grab for it in these types of cases. Hopefully you now feel like you have a new tool in your belt.

The post Managing State in React With Unstated appeared first on CSS-Tricks.

Understanding React `setState`

React components can, and often do, have state. State can be anything, but think of things like whether a user is logged in or not and displaying the correct username based on which account is active. Or an array of blog posts. Or if a modal is open or not and which tab within it is active.

React components with state render reconciliation. The reconciliation process is the way React updates the DOM, by making changes to the component based on the change in state. When the request to setState() is triggered, React creates a new tree containing the reactive elements in the component (along with the updated state). This tree is used to figure out how the Search component’s setState Pen by Kingsley Silas Chijioke (@kinsomicrote) on CodePen.

Let’s register the component and define the markup for the Understanding React `setState` appeared first on CSS-Tricks.

React State From the Ground Up

As you begin to learn React, you will be faced with understanding what state is. State is hugely important in React, and perhaps a big reason you’ve looked into using React in the first place. Let’s take a stab at understanding what state is and how it works.

What is State?

State, in React, is a plain JavaScript object that allows you keep track of a component’s data. The state of a component can change. A change to the state of a component depends on the functionality of the application. Changes can be based on user response, new messages from server-side, network response, or anything.

Component state is expected to be private to the component and controlled by the same component. To make changes to a component’s state, you have to make them inside the component — the initialization and updating of the component’s state.

Class Components

States is only available to components that are called class components. The main reason why you will want to use class components over their counterpart, functional components, is that class components can have state. Let’s see the difference. Functional components are JavaScript functions, like this:

const App = (props) => { return ( <div> { this.props } </div> )
}

If the functionality you need from your component is as simple as the one above, then a functional component is the perfect fit. A class component will look a lot more complex than that.

class App extends React.Component { constructor(props) { super(props) this.state = { username: 'johndoe' } } render() { const { username } = this.state return( <div> { username } </div> ) }
}

Above, I am setting the state of the component’s username to a string.

The Constructor

According to the official documentation, the constructor is the right place to initialize state. Initializing state is done by setting this.state to an object, like you can see above. Remember: state is a plain JavaScript object. The initial state of the App component has been set to a state object which contains the key username, and its value johndoe using this.state = { username: 'johndoe' }.

Initializing a component state can get as complex as what you can see here:

constructor(props) { super(props) this.state = { currentTime: 0, status: false, btnOne: false, todoList: [], name: 'John Doe' }
}

Accessing State

An initialized state can be accessed in the render() method, as I did above.

render() { const { username } = this.state return( <div> { username } </div> )
}

An alternative to the above snippet is:

render() { return( <div> { this.state.username } </div> )
}

The difference is that I extracted the username from state in the first example, but it can also be written as const status = this.state.username. Thanks to ES6 destructuring, I do not have to go that route. Do not get confused when you see things like this. It is important to know that I am not reassigning state when I did that. The initial setup of state was done in the constructor, and should not be done again – never update your component state directly.

A state can be accessed using this.state.property-name. Do not forget that aside from the point where you initialized your state, the next time you are to make use of this.state is when you want to access the state.

Updating State

The only permissible way to update a component’s state is by using setState(). Let’s see how this works practically.

First, I will start with creating the method that gets called to update the component’s username. This method should receive an argument, and it is expected to use that argument to update the state.

handleInputChange(username) { this.setState({username})
}

Once again, you can see that I am passing in an object to setState(). With that done, I will need to pass this function to the event handler that gets called when the value of an input box is changed. The event handler will give the context of the event that was triggered which makes it possible to obtain the value entered in the input box using event.target.value. This is the argument passed to handleInputChange() method. So, the render method should look like this.

render() { const { username } = this.state return ( <div> <div> <input type="text" value={this.state.username} onChange={event => this.handleInputChange(event.target.value)} /> </div> <p>Your username is, {username}</p> </div> )
}

Each time setState() is called, a request is sent to React to update the DOM using the newly updated state. Having this mindset makes you understand that state update can be delayed.

Your component should look like this;

class App extends React.Component { constructor(props) { super(props) this.state = { username: 'johndoe' } } handleInputChange(username) { this.setState({username}) } render() { const { username } = this.state return ( <div> <div> <input type="text" value={this.state.username} onChange={event => this.handleInputChange(event.target.value)} /> </div> <p>Your username is, {username}</p> </div> ) }
}

Passing State as Props

A state can be passed as props from a parent to the child component. To see this in action, let’s create a new component for creating a To Do List. This component will have an input field to enter daily tasks and the tasks will be passed as props to the child component.

Try to create the parent component on your own, using the lessons you have learned thus far.

Let’s start with creating the initial state of the component.

class App extends React.Component { constructor(props) { super(props) this.state = { todoList: [] } } render() { return() }
}

The component’s state has its todoList set to an empty array. In the render() method, I want to return a form for submitting tasks.

render() { const { todoList } = this.state return ( <div> <h2>Enter your to-do</h2> <form onSubmit={this.handleSubmit}> <label>Todo Item</label> <input type="text" name="todoitem" /> <button type="submit">Submit</button> </form> </div > )
}

Each time a new item is entered and the submit button is clicked, the method handleSubmit gets called. This method will be used to update the state of the component. The way I want to update it is by using concat to add the new value in the todoList array. Doing so will set the value for todoList inside the setState() method. Here’s how that should look:

handleSubmit = (event) => { event.preventDefault() const value = (event.target.elements.todoitem.value) this.setState(({todoList}) => ({ todoList: todoList.concat(value) }))
}

The event context is obtained each time the submit button is clicked. We use event.preventDefault() to stop the default action of submission which would reload the page. The value entered in the input field is assigned a variable called value, which is then passed an argument when todoList.concat() is called. React updates the state of todoList by adding the new value to the initial empty array. This new array becomes the current state of todoList. When another item is added, the cycle repeats.

A chart illustrating the cycle explained above.

The goal here is to pass the individual item to a child component as props. For this tutorial, we’ll call it the TodoItem component. Add the code snippet below inside the parent div which you have in render() method.

<div> <h2>Your todo lists include:</h2> { todoList.map(i => <TodoItem item={i} /> )}
</div>

You’re using map to loop through the todoList array, which means the individual item is then passed to the TodoItem component as props. To make use of this, you need to have a TodoItem component that receives props and renders it on the DOM. I will show you how to do this using functional and class components.

Written as a functional component:

const TodoItem = (props) => { return ( <div> {props.item} </div> )
}

For the class component, it would be:

class TodoItem extends React.Component { constructor(props) { super(props) } render() { const {item} = this.props return ( <div> {item} </div> ) }
}

If there is no need to manage state in this component, you are better off using functional component.

Leveling Up

You will be handling state very often while developing React application. With all the areas covered above, you should have the confidence of being able to dive into the advanced part of state management in React. To dig deeper, I recommend React’s official documentation on State and Lifecycle as well as Uber’s React Guide on Props vs State.

The post React State From the Ground Up appeared first on CSS-Tricks.