# Redux-like state management: Let's code a Virtual DOM! #2

State management is a crucial feature of any modern application. 

Even though there are many approaches to managing the state, in this post, we will focus on just one solution.

We will try to implement a state management system similar to Redux (heavily inspired by [elm-lang](https://guide.elm-lang.org/architecture/)).

## Where we left off

In the first article from this series, we wrote a simple virtual dom that renders some elements on the page. 

If you haven't seen part 1, make sure to read it first. [You can find it here.](https://kkalamarski.me/lets-code-a-virtual-dom)

The code we've written until this point can be found here:

%[https://stackblitz.com/edit/vitejs-vite-9tphin?embed=1&file=src/main.ts]

Now, let's get started.

## Creating a Store

To manage the state we will create a `Store` class.

It will handle:
* keeping the state
* dispatching actions
* updating the state
* listening for state changes

To keep things nice and clean, let's create a separate file for it: `store.ts`

It will contain a default export of a `Store` class:

```typescript
export default class Store<T> {
  // ...
}
```

It's a generic class that takes one type argument: 
* `T` - the type of a model 

Now let's implement the constructor for our class:

```typescript
constructor(private state: T, private update: UpdateFn<T>) {}
```

In the constructor we declare two fields: 
* `state` - that will be our initial state 
* `update` - update function that will be fired every time an action is dispatched. In the Redux world, it's called a "reducer".

Update function has following type signature:

```typescript
type UpdateFn<T> = (state: T, action: any) => T;
```

Next, we need a way to listen for state changes in the store - for this, we need to create a private field `listeners` and a public method `subscribe`:

```typescript
private listeners: Array<(state: T) => void> = [];

subscribe(listener: (state: T) => void) {
  this.listeners.push(listener);
}
```

Now the only thing that's left - we need to implement a `dispatch` method:

```typescript
dispatch(action: any) {
  this.state = this.update(this.state, action);

  this.listeners.forEach((listener) => listener(this.state));
}
```

The dispatch method takes an action as an argument and then runs the update function with the current state and said action. 

The result of the update function will be a new state, that is assigned back to the store.

The next step is to trigger all listeners with the updated state.

Now that we have this method implemented, we need to asynchronously dispatch an `INIT_STORE` action (it can be called whatever you want) to trigger the listeners for the first time.

Back in the constructor:

```typescript
constructor(private state: T, private update: UpdateFn<T>) {
  setTimeout(() => {
    this.dispatch(['INIT_STORE']);
  });
}
```

That's it - we have a fully functional store now!

Here is the full code:

```typescript
// store.ts

type UpdateFn<T> = (state: T, action: any) => T;

export default class Store<T> {
  private listeners: Array<any> = [];

  constructor(private state: T, private update: UpdateFn<T>) {
    setTimeout(() => {
      this.dispatch(['INIT_STORE']);
    });
  }

  subscribe(listener: (state: T) => void) {
    this.listeners.push(listener);
  }

  dispatch(action: any) {
    this.state = this.update(this.state, action);

    this.listeners.forEach((listener) => listener(this.state));
  }
}
```

Now, how do I use it?

## Subscribing to the Store

Let's go back to our `main.ts` file.

In the last post we've implemented the following code:

```typescript
const render = (root: HTMLElement, view: Function) => {
  const rendered = view();

  diff(root, null, rendered);
};
```

To add a store to our virtual DOM we need to change a few things here:
* pass a `model` and `update` function alongside `view` function
* register a store
* run our `view` and `diff` functions whenever the state changes

First, let's add a few type annotations:

```typescript
type Element = { type: string; props: any; children: Element[] } | string;

type Application<T> = {
  model: T;
  view: (state: T, dispatch: Function) => Element;
  update: (state: T, action: any) => T;
};
```

`Element` type is a return value of a function `h` we declared last week.

And an `Application<T>` type declares our application - `model`, `view` and `update` functions.

Let's see how it can be implemented:

```typescript
const render = (
  root: HTMLElement,
  { model, view, update }: Application<number>
) => {
  // ...
}
```

Inside the render function we want to register a store:

```typescript
import Store from './store'; 

// ...

const render = (
  root: HTMLElement,
  { model, view, update }: Application<number>
) => {
  const store = new Store(model, update);
  
  // ...
}
```

The store is initialized with the `model` and `update` function. `model` will serve an initial state, and the `update` function will update that state every time an action is dispatched on the store.

Next, we need to subscribe to the store changes:

```typescript
const render = (
  root: HTMLElement,
  { model, view, update }: Application<number>
) => {
  const store = new Store(model, update);

  store.subscribe((state) => {
    const rendered = view(state, store.dispatch.bind(store));

    setTimeout(() => {
      diff(root, null, rendered);
    });
  });
};
```

We call the `subscribe` method on the store, passing a `listener` function: It will be run every time the state changes.

In the listener function we run our `view` function - to render new virtual DOM, and then we reconcile it with the current DOM state via the `diff` function.

Notice that we are now passing two arguments to the `view` function - a current state and a dispatch function.

That way we can access them both directly inside our virtual dom.

## Virtual DOM with state management

Now we are ready to connect everything together.

Let's see how can we run the `render` function that we modified.

```typescript
render(app, {
  model: 1,
  view(state, dispatch) {
    setTimeout(() => dispatch('increment'), 1000);

    return h('div', {}, [
      h('h1', {}, ['Hello']),
      h('p', {}, ['The value is: ', String(state)]),
    ]);
  },
  update(state, action) {
    switch (action) {
      case 'increment':
        return state + 1;
      default:
        return state;
    }
  },
});
```

Doesn't look so complex! 
Let's take a look at the entire code in the `main` file:

```typescript
import Store from './store';

const app = document.querySelector<HTMLDivElement>('#app')!;

const h = (type: string, props: any = {}, children: any[] = []) => ({
  type,
  props,
  children,
});

type Element = { type: string; props: any; children: Element[] } | string;

type Application<T> = {
  model: T;
  view: (state: T, dispatch: Function) => Element;
  update: (state: T, action: any) => T;
};

const render = (
  root: HTMLElement,
  { model, view, update }: Application<number>
) => {
  const store = new Store(model, update);

  store.subscribe((state) => {
    const rendered = view(state, store.dispatch.bind(store));

    setTimeout(() => {
      diff(root, null, rendered);
    });
  });
};

const createElement = (node: any) => {
  if (typeof node === 'string') {
    return document.createTextNode(node);
  }

  const el = document.createElement(node.type);
  node.children.map(createElement).forEach(el.appendChild.bind(el));

  return el;
};

const diff = (
  root: HTMLElement,
  oldNode: any,
  newNode: any,
  index: number = 0
) => {
  if (!oldNode) {
    root.appendChild(createElement(newNode));
  }
};

render(app, {
  model: 1,
  view(state, dispatch) {
    setTimeout(() => dispatch('increment'), 1000);

    return h('div', {}, [
      h('h1', {}, ['Hello']),
      h('p', {}, ['The value is: ', String(state)]),
    ]);
  },
  update(state, action) {
    switch (action) {
      case 'increment':
        return state + 1;
      default:
        return state;
    }
  },
});
```

It's starting to feel a little cluttered - we need to do some refactoring.

Let's move the `h` and `render` functions (with their type annotations) to a separate file called `dom.ts`.

Next, we can move the `diff` and `createElement` functions to a separate file named `diff.ts`

The result is a clean app implementation in `main.ts`:

```typescript
import { h, render } from './dom';

const app = document.querySelector<HTMLDivElement>('#app')!;

render(app, {
  model: 1,
  view(state, dispatch) {
    setTimeout(() => dispatch('increment'), 1000);

    return h('div', {}, [
      h('h1', {}, ['Hello']),
      h('p', {}, ['The value is: ', String(state)]),
    ]);
  },
  update(state, action) {
    switch (action) {
      case 'increment':
        return state + 1;
      default:
        return state;
    }
  },
});
```
The whole code can be viewed here:

%[https://stackblitz.com/edit/vitejs-vite-sa61bx?embed=1&file=src/main.ts&view=editor]

## Problem
If you run this code you will see that it's not working as intended.
Each second the state is updated, but instead of replacing the old DOM node, a new one is added.

![Zrzut ekranu 2022-04-24 o 01.17.04.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1650755828613/94ElgEXEC.png)

That's because, in the last instalment of this series, we only handled adding new items to DOM in the `diff` function! 

In the next post from this series, we'll fix that - we will implement a full reconciliation algorithm to make sure that the DOM is always up to date with our Virtual DOM.

Until then, please let me know what you think about this project. 
Did you find it interesting?

Hope to hear your feedback! Stay tuned for the next posts in this series!


