Combining the Command Pattern with State Pattern in JavaScript
Tags: javascript, webdev, coding, node
Published: 2022-11-24 17:59:00

---
title: Combining the Command Pattern with State Pattern in JavaScript
published: true
description: We are going to incorporate/combine the command pattern together with the state pattern.
tags:
  - javascript
  - webdev
  - coding
  - nodejs
cover_image: https://jsmanifest.s3.us-west-1.amazonaws.com/posts/combining-the-command-pattern-with-state-pattern-in-javascript/thumbnail.jpg
published_at: 2022-11-24 17:59 +0000
---

JavaScript is a popular language known for its _flexibility_. It's thanks to this that makes patterns like the command pattern easier to implement in our apps.

When there's a design pattern out there that goes well hand-in-hand with the state pattern, it's arguably the command pattern.

If you've read one of my previous blog posts about the state pattern you might remember this sentence: "The State Pattern ensures an object to behave in a predictable, coordinated way depending on the current "state" of the application."

In the command pattern the main goal is to separate the communication between two important participants:

1. The initiator (also called the invoker)
2. The handler

We are going to incorporate the command pattern together with the state pattern in this post. If you're learning either of the two, my best advice to you in order to get the most out of this post is to make sure you understand the flow of the state pattern before continuing to the command pattern implementation to better feel how the behavior of the code drastically changes while the functionality remains in tact.

Let's start off with an example using the state pattern so that we can see this in a clearer perspective:

```js
let state = { backgroundColor: 'white', profiles: [] }
let subscribers = []

function notifySubscribers(...args) {
  subscribers.forEach((fn) => fn(...args))
}

function setBackgroundColor(color) {
  setState((prevState) => ({
    ...prevState,
    backgroundColor: color,
  }))
}

function setState(newState) {
  let prevState = state
  state =
    typeof newState === 'function'
      ? newState(prevState)
      : { ...prevState, ...newState }

  notifySubscribers(prevState, state)
}

function subscribe(callback) {
  subscribers.push(callback)
}

function addProfile(profile) {
  setState((prevState) => ({
    ...prevState,
    profiles: prevState.profiles.concat(profile),
  }))
}

subscribe(
  (function () {
    function getColor(length) {
      if (length >= 5 && length <= 8) return 'blue' // Average
      if (length > 8 && length < 10) return 'orange' // Reaching limit
      if (length > 10) return 'red' // Limit reached
      return 'white' // Default
    }

    return (prevState, newState) => {
      const prevProfiles = prevState?.profiles || []
      const newProfiles = newState?.profiles || []
      if (prevProfiles.length !== newProfiles.length) {
        setBackgroundColor(getColor(newProfiles.length))
      }
    }
  })(),
)

console.log(state.backgroundColor) // 'white'

addProfile({ id: 0, name: 'george', gender: 'male' })
addProfile({ id: 1, name: 'sally', gender: 'female' })
addProfile({ id: 2, name: 'kelly', gender: 'female' })

console.log(state.backgroundColor) // 'white'

addProfile({ id: 3, name: 'mike', gender: 'male' })
addProfile({ id: 4, name: 'bob', gender: 'male' })

console.log(state.backgroundColor) // 'blue'

addProfile({ id: 5, name: 'kevin', gender: 'male' })
addProfile({ id: 6, name: 'henry', gender: 'male' })

console.log(state.backgroundColor) // 'blue'

addProfile({ name: 'ronald', gender: 'male' })
addProfile({ name: 'chris', gender: 'male' })
addProfile({ name: 'andy', gender: 'male' })
addProfile({ name: 'luke', gender: 'male' })

console.log(state.backgroundColor) // 'red'
```

In the example above we have a `state` and `subscribers` object. The `subscribers` object holds a collection of callback functions. These callback functions are called whenever the `setState` function is called:

![state-pattern-notify-subscriber-callbacks-in-javascript](https://jsmanifest.s3.us-west-1.amazonaws.com/posts/combining-the-command-pattern-with-state-pattern-in-javascript/state-pattern-notify-subscriber-callbacks-in-javascript.png)

Every time the state updates, all of the registered callbacks are called with the previous state (`prevState`) and the new state (`newState`) in arguments.

We registered one callback listener so that we can watch updates to the state and update the background color whenever the amount of profiles hits a certain `length`. The table below shows a clearer picture lining up the profile counts with the associated color:

| Minimum Threshold | Background Color |
| ----------------- | ---------------- |
| 0                 | white            |
| 5                 | blue             |
| 9                 | orange           |
| 10                | red              |

![state-pattern-subscribe-callback-handler-on-change](https://jsmanifest.s3.us-west-1.amazonaws.com/posts/combining-the-command-pattern-with-state-pattern-in-javascript/state-pattern-subscribe-callback-handler-on-change.png)

So how can the command pattern fit into this? Well, if we look back into our code we can see that we defined some functions responsible for both calling and handling this logic:

```js
function notifySubscribers(...args) {
  subscribers.forEach((fn) => fn(...args))
}

function setBackgroundColor(color) {
  setState((prevState) => ({
    ...prevState,
    backgroundColor: color,
  }))
}

function addProfile(profile) {
  setState((prevState) => ({
    ...prevState,
    profiles: prevState.profiles.concat(profile),
  }))
}
```

We can abstract these into commands instead. In the upcoming code example it will show the same code with the command pattern implemented in harmony with the state pattern. When we do that there are only 2 functions left untouched: `setState` and `subscribe`.

Let's go ahead and introduce the command pattern and commandify our abstracted functions:

```js
let state = { backgroundColor: 'white', profiles: [] }
let subscribers = []
let commands = {}

function setState(newState) {
  let prevState = state
  state =
    typeof newState === 'function'
      ? newState(prevState)
      : { ...prevState, ...newState }

  dispatch('NOTIFY_SUBSCRIBERS', { prevState, newState: state })
}

function subscribe(callback) {
  subscribers.push(callback)
}

function registerCommand(name, callback) {
  if (commands[name]) commands[name].push(callback)
  else commands[name] = [callback]
}

function dispatch(name, action) {
  commands[name]?.forEach?.((fn) => fn?.(action))
}

registerCommand(
  'SET_BACKGROUND_COLOR',
  function onSetState({ backgroundColor }) {
    setState((prevState) => ({ ...prevState, backgroundColor }))
  },
)

registerCommand('NOTIFY_SUBSCRIBERS', function onNotifySubscribers(...args) {
  subscribers.forEach((fn) => fn(...args))
})

registerCommand('ADD_PROFILE', function onAddProfile(profile) {
  setState((prevState) => ({
    ...prevState,
    profiles: prevState.profiles.concat(profile),
  }))
})

subscribe(
  (function () {
    function getColor(length) {
      if (length >= 5 && length <= 8) return 'blue' // Average
      if (length > 8 && length < 10) return 'orange' // Reaching limit
      if (length > 10) return 'red' // Limit reached
      return 'white' // Default
    }

    return ({ prevState, newState }) => {
      const prevProfiles = prevState?.profiles || []
      const newProfiles = newState?.profiles || []
      if (prevProfiles.length !== newProfiles.length) {
        dispatch('SET_BACKGROUND_COLOR', {
          backgroundColor: getColor(newProfiles.length),
        })
      }
    }
  })(),
)

console.log(state.backgroundColor) // 'white'

dispatch('ADD_PROFILE', { id: 0, name: 'george', gender: 'male' })
dispatch('ADD_PROFILE', { id: 1, name: 'sally', gender: 'female' })
dispatch('ADD_PROFILE', { id: 2, name: 'kelly', gender: 'female' })

console.log(state.backgroundColor) // 'white'

dispatch('ADD_PROFILE', { id: 3, name: 'mike', gender: 'male' })
dispatch('ADD_PROFILE', { id: 4, name: 'bob', gender: 'male' })

console.log(state.backgroundColor) // 'blue'

dispatch('ADD_PROFILE', { id: 5, name: 'kevin', gender: 'male' })
dispatch('ADD_PROFILE', { id: 6, name: 'henry', gender: 'male' })

console.log(state.backgroundColor) // 'blue'

dispatch('ADD_PROFILE', { id: 7, name: 'ronald', gender: 'male' })
dispatch('ADD_PROFILE', { id: 8, name: 'chris', gender: 'male' })
dispatch('ADD_PROFILE', { id: 9, name: 'andy', gender: 'male' })
dispatch('ADD_PROFILE', { id: 10, name: 'luke', gender: 'male' })

console.log(state.backgroundColor) // 'red'
```

It's much clearer now determining which functions we need to update the state. We can separate everything else into their own separate command handlers. That way we can isolate them to be in their own separate file or location so we can work with them easier.

Here were the steps demonstrated in our updated example to get to that point:

1. Create the `commands` variable. This will store registered commands and their callback handlers.
2. Define `registerCommand`. This will register new commands and their callback handlers to the `commands` object.
3. Define `dispatch`. This is responsible for _calling_ the callback handlers associated with their command.

With these three steps we perfectly set up our commands to be registered by the client code, allowing them to implement their own commands and logic. Notice how how our `registerCommand` and `dispatch` functions don't need to be aware of anything related to our state objects.

We can easily take advantage of that and continue with isolating them into a separate file:

> commands.js

```js
// Private object hidden within this scope
let commands = {}

export function registerCommand(name, callback) {
  if (commands[name]) commands[name].push(callback)
  else commands[name] = [callback]
}

export function dispatch(name, action) {
  commands[name]?.forEach?.((fn) => fn?.(action))
}
```

As for the actual logic written in these lines:

```js
registerCommand(
  'SET_BACKGROUND_COLOR',
  function onSetState({ backgroundColor }) {
    setState((prevState) => ({ ...prevState, backgroundColor }))
  },
)

registerCommand('NOTIFY_SUBSCRIBERS', function onNotifySubscribers(...args) {
  subscribers.forEach((fn) => fn(...args))
})

registerCommand('ADD_PROFILE', function onAddProfile(profile) {
  setState((prevState) => ({
    ...prevState,
    profiles: prevState.profiles.concat(profile),
  }))
})
```

Normally this would all be left to the client code to decide that (technically, our last code snippet _are_ representing the client code). Some library authors also define their own command handlers for internal use but the same concept still applies. One practice I see often is having their internal logic put into a separate file with its file name prefixed with `"internal"` (example: `internalCommands.ts`). It's worth noting that 99% of the time the functions in those files are never exported out to the user. That's why they're marked _internal_.

The image below is a diagram of what our code looked like _before_ implementing the command design pattern to it:

![state-pattern-before-command-pattern-integration](https://jsmanifest.s3.us-west-1.amazonaws.com/posts/combining-the-command-pattern-with-state-pattern-in-javascript/state-pattern-before-command-pattern-integration.png)

The bubbles in the purple represent functions. The two functions `setBackgroundColor` and `addProfile` are included amongst them. Specifically for those two however call `setState` directly to facilitate changes to state. In other words they _both call and handle the state update logic_ to specific slices of the state they're interested in.

Now have a look at the diagram below. This image shows how our code looks like _after_ implementing the pattern:

![command-design-pattern-together-with-state-pattern](https://jsmanifest.s3.us-west-1.amazonaws.com/posts/combining-the-command-pattern-with-state-pattern-in-javascript/command-design-pattern-together-with-state-pattern.png)

The functions `notifySubscribers`, `addProfile`, and `setBackgroundColor` are gone, but all of their logic still remains. They're now written as command handlers:

![command-design-pattern-together-with-state-pattern-with-indicators](https://jsmanifest.s3.us-west-1.amazonaws.com/posts/combining-the-command-pattern-with-state-pattern-in-javascript/command-design-pattern-together-with-state-pattern-with-indicators.png)

Command handlers define their logic _separately_ and become registered. Once they're registered, they are "put on hold" until they are called by the `dispatch` function.

Ultimately, the code functionality stays the same and only the behavior was changed.

## Who uses this approach?

One example that immediately pops up in my mind is the [lexical](https://github.com/facebook/lexical) package by Facebook [here](https://github.com/facebook/lexical/blob/main/packages/lexical/src/LexicalCommands.ts). Lexical is an "extensible JavaScript web text-editor framework with an emphasis on reliability, accessibility, and performance".

In lexical, commands for the editor can be [registered](https://github.com/facebook/lexical/blob/main/packages/lexical/src/LexicalEditor.ts#L584) and become available for use. The handling logic is defined when they get registered so they can be identified for the `dispatch` call.

## Conclusion

And that concludes the end of this post! I hope you found this to be valuable and look out for more in the future!