Create Your Effector-like State Manager ☄️
Tags: javascript, react, webdev, tutorial
Published: 2020-10-23 18:55:43

---
title: Create Your Effector-like State Manager ☄️
published: true
description: We have a lot of state managers in our Javascript-World and use them everyday, but now it's a time to understand "How they work"
tags: #javascript #react, #webdev, #tutorial
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/7d3qzzahw3d18r7m5ux8.png
---
## **Introduction**

We have a lot of state managers in our Javascript-World and use them every day, but now it's a time to understand "How they work".

There's a very nice state manager [Effector.js](https://effector.dev/) which is very simple to use in your application, also it's easy to replace your current global state manager. So, I recommend to use it for your project and doesn't matter for which framework. I use it on my React Apps.

## **Let's start to create own Effector-like state manager!**

*We're gonna create basics looks like Effector, of course Effector is deeper and complex and our state manager is just simple version of it.*

1) Firstly, let's create a js file (`effector-clone.js`) with our state manager. We start with `createStore` function that create our **store** instance with **events**.

```js
export const createStore = initState => {
  let state = initState; // State of instance
	let events = new Map(); // Events subscribed to the current store
	let watchers = []; // Array of watcher that get called on the state changes

  let store = {
    getState: () => state, // Get current state of the store
		on(event, cb) {}, // Method to subscribe event
		dispatch(event, payload) {}, // Dispatch event to make changes in the store
		watch(cb) {} // Subscribe to the state changes
  };

  return store;
};
```

2) We need to add a function that creates **event instance**.

*Let's add this code into the file above!*

```js
export const createEvent = () => {
	// Return function that iterates stores linked to the event
  let event = payload => {
    event.stores.forEach((store) => {
      store.dispatch(event, payload);
    });
  };

	// Let's link stores to the event
	// We're gonna call stores' dispatches on event's call
  event.stores = [];

  return event;
};
```

3) Implement **on,** **dispatch and watch** methods for the store instance.

```js
export const createStore = initState => {
  let state = initState;
	let events = new Map();
	let watchers = [];

  let store = {
    getState: () => state,
		on(event, cb) {
			// Subscribe to store
			// We use event instance as key for map and callback as a value
			// [event: Function]: callback: Function

			// Set event in the events map if it hasn't the event in this store
			if (!events.has(event)) {
        events.set(event, cb);
				event.stores.push(this);
      }
      return this;
		},
		dispatch(event, payload) {
			// We get and call event's callback and
			// set it's result to the store's state

			const cb = events.get(event);
      if (cb && typeof cb === "function") {
        let newState = cb(state, payload);

				// Check if state is the same
        if (newState !== state) {
          state = newState;
        }
      }
	
			// Iterable callbacks on the state changes
			watchers.forEach((watch) => watch(state, payload));
		},
		watch(cb) {
      watchers.push(cb);
			// Return function to unsubscribe the watcher
      return () => {
        watchers = watchers.filter((i) => i !== cb);
      };
    }
  };

  return store;
};
```

**Core part of our state manager is Done! ✅**

## **Use it with React + hooks  ⚛︎**

We're gonna use it as a global state manager. It's also okay to use inside your component.

1) Create `useStore.js` file and add this simple code.

```js
import { useEffect, useState } from "react";

export const useStore = store => {
	// We get initial state of the store
  const [state, setState] = useState(store.getState());

  useEffect(() => {
		// Pass setState function as a callback
		// store.watch() returns unsubscribe function
    const unsubscribe = store.watch(setState);

    return () => {
			// Unsubscribe our watcher on component unmount
      unsubscribe();
    };
  }, [store]);

  return state;
};
```

2) Create `counterStore.js` file with our counter store

```js
import { createStore, createEvent } from "./effector-clone";

export const $counter = createStore(0);

export const inc = createEvent();
export const dec = createEvent();
export const reset = createEvent();

$counter
  .on(inc, (state) => state + 1)
  .on(dec, (state) => state - 1)
  .on(reset, () => 0);
```

3) Create a `Counter.jsx` component

```jsx
import React from "react";
import { $counter, inc, dec, reset } from "./counterStore";
import { useStore } from "./useStore";

export const Counter = () => {
  const total = useStore($counter);

  return (
    <>
      <p>Total: <b>{total}</b></p>
      <button onClick={dec}>-</button>
      <button onClick={reset}>Reset</button>
      <button onClick={inc}>+</button>
    </>
  );
};
```

**Magic works ✨✨✨**

## **Conclusion**

We created own custom state manager and **its size with useStore hook is only 1.4kb.** I hope now it's a bit cleaner about how it works and how to create own state manager. Of course it needs upgrades and error handlers, but depends on your feedbacks I'll publish an article about these upgrades.

Try it on [codesandbox](https://codesandbox.io/s/awesome-swirles-5d93c?file=/src/effector-clone.js)! 🔥

**Thank You For Reading!**
<br />
by [Orkhan Jafarov](https://github.com/gigantz)