A tiny event-based state manager Storeon for Velo
Tags: wix, velo, wixcode
Published: 2021-01-11 08:42:53

# A tiny event-based state manager Storeon for Velo

*In this article, we explain how to manage a state in Velo with a light-weight and robust solution: Storeon, an event-based state manager.*

## Motivation

In the article, [“State management in Corvid”](https://medium.com/@shahata/state-management-in-corvid-2ebfa8740abd), Shahar Talmi brings up a question about controlling app states in Velo. If you’re not familiar with Velo, it’s a development platform running on Wix that allows you to quickly and easily develop web applications.

Accurately controlling the state of any app is a really big problem. If you have many component dependencies or need to handle constant user interactions, you're going to suffer a bit when you want to eventually add a new feature or scale your application.

In this article, I share my solution — a very tiny library called [Storeon](https://evilmartians.com/chronicles/storeon-redux-in-173-bytes) (it’s only 180 bytes) that features an easy interface. So, I wrote a wrapper for integration with Velo. As a result, we have the state manager [storeon-velo](https://github.com/shoonia/storeon-velo), and it’s less than 90 lines of code.

## How it works

We will create a traditional study app with counters. I will use two counters to help provide a better demonstration.

At first, we need to install the library from [Package Manager](https://support.wix.com/en/article/velo-working-with-npm-packages)

![Package Manager panel in Wix editor, installing storeon-velo](https://dev-to-uploads.s3.amazonaws.com/i/fzadg09u3bkgktz4ncm1.jpg)

and create one more file for store initialization in the **public** folder.

```tree
public
└── store.js
```

We will write our business logic in `public/store.js`.

Storeon's state is always an object; it canʼt be anything else. Itʼs a small limitation and not too important to us, but we have to remember it.

**public/store.js**

```js
// The store should be created with createStoreon() function.
// It accepts a list of the modules.
import { createStoreon } from 'storeon-velo';

// Each module is just a function,
// which will accept a store and bind their event listeners.
const counterModule = (store) => {
  // @init will be fired in createStoreon.
  // The best moment to set an initial state.
  store.on('@init', () => ({ x: 0, y: 0 }));

  // Reducers returns only changed part of the state
  // You can dispatch any other events.
  // Just do not start event names with @.
  store.on('INCREMENT_X', (state) => ({ x: state.x + 1 }));
  store.on('DECREMENT_X', (state) => ({ x: state.x - 1 }));

  store.on('INCREMENT_Y', (state) => ({ y: state.y + 1 }));
  store.on('DECREMENT_Y', (state) => ({ y: state.y - 1 }));
}

// createStoreon() returns 4 methods to work with store
export const {
  getState, // <- will return current state.
  dispatch, // <- will emit an event with optional data.
  connect, // <- connect to state by property key.
  connectPage, // <- wrapper around $w.onReady()
} = createStoreon([counterModule]);
```

So, we created a store in the public folder and exported from there with four methods. In the second part, we will create our UI, and we will write logic to change the state.

Letʼs add two text elements to display our counter value and four buttons for event increments/decrements.

![Example: Wix Editor layouts with two counters for increment and decrement](https://dev-to-uploads.s3.amazonaws.com/i/8tqjgctk9ag2kh5zxdxd.png)

Of course, we have to import the store methods from the public file to the page's code.

```js
import { dispatch, connect, connectPage } from 'public/store';
```

With `connect("key", callback)`, we can subscribe to any store properties, and the callback function will be run when the page is loaded and each time when the listed property changes.

The `connectPage(callback)` is a wrapper around the `$w.onReady(callback)`. With `dispatch(event, [data])`, we will emit events.

**Page Code**

```js
import { getState, dispatch, connect, connectPage } from 'public/store';

// Connect to property "x".
// The callback function will be run when the page loads ($w.onReady())
// and each time when property "x" would change.
connect('x', (state) => {
  console.log('counter X is changed', state);

  $w('#textX').text = String(state.x);
});

// Connect to "y"
connect('y', (state) => {
  console.log('counter Y is changed', state);

  $w('#textY').text = String(state.y);
});

// Wrapper around $w.onReady()
// The callback function will be run once.
connectPage((state) => {
  // Here we also have an object with initial state
  console.log('onReady runs', state);

  // X counter events
  $w('#buttonIncX').onClick(() => {
    dispatch('INCREMENT_X');
  });

  $w('#buttonDecX').onClick(() => {
    dispatch('DECREMENT_X');
  });

  // Y counter events
  $w('#buttonIncY').onClick(() => {
    dispatch('INCREMENT_Y');
  });

  $w('#buttonDecY').onClick(() => {
    dispatch('DECREMENT_Y');
  });
});
```

**[Live Demo](https://www.wix.com/alexanderz5/storeon-velo)**

## Modules

The function, `createStoreon(modules)`, accepts a list of modules. We can create different functions to split business logic into our app. Letʼs see a few examples:

Synchronization the App state with the `wix-storage` memory API:

```js
// https://www.wix.com/velo/reference/wix-storage/memory
import { memory } from 'wix-storage';

export const memoryModule = (store) => {
  // @changed will be fired every when event listeners changed the state.
  // It receives object with state changes.
  store.on('@changed', (state) => {
    memory.setItem('key', JSON.stringify(state));
  });
}
```

Tracking an event to external analytics tools with `wixWindow.trackEvent()`:

```js
import wixWindow from 'wix-window';

export const trackEventModule = (store) => {
  // @dispatch will be fired on every `dispatch(event, [data])` call.
  // It receives an array with the event name and the event’s data.
  // Can be useful for debugging.
  store.on('@dispatch', (state, [event, data]) => {
    if (event === 'product/add') {
      // Sends a tracking event to external analytics tools.
      wixWindow.trackEvent('CustomEvent', { event, data });
    }
  });
}
```

Combining modules

```js
const store = createStoreon([
  coutnerModule,
  memoryModule,
  trackEventModule,
]);
```

## Conclusion

As you can see, we were able to quickly implement our state management solution with a minimal amount of code. Of course, due to data binding in Velo, you normally don’t have to worry about state management. However, in more complex applications, the issue can become more difficult, and state management will become more challenging to handle.

State management can be a tricky problem, but Storeon offers a simple, yet robust solution. In addition, Velo allows us to quickly implement this in our application, all while focusing on code and not having to spend time dealing with other issues.

## Resources

- [Storeon](https://evilmartians.com/chronicles/storeon-redux-in-173-bytes)
- [Storeon on GitHub](https://github.com/storeon/storeon)
- [Storeon Velo on GitHub](https://github.com/shoonia/storeon-velo)
- [Discussion on Velo Forum](https://www.wix.com/velo/forum/community-discussion/a-tiny-event-based-state-manager-storeon-for-velo)

## Demo

- [Site Live Demo](https://www.wix.com/alexanderz5/storeon-velo)
- [Open In Editor example Storeon Velo](https://editor.wix.com/html/editor/web/renderer/new?siteId=d6003ab4-7b91-4fe1-b65e-55ff3baca1f4&metaSiteId=654936ba-93bc-4f97-920a-c3050dd82fe7&autoDevMode=true)

## Also

- [“State management in Corvid” by Shahar Talmi](https://medium.com/@shahata/state-management-in-corvid-2ebfa8740abd)