Skip to content

svi3c/rx-simple-store

Repository files navigation

Build Status

rx-simple-store

This is an isomorphic distributed rxjs 5 implementation of a reactive store, written in TypeScript.

History

I was originally inspired by the article Redux in a single line of code with RxJS by Rudi Yardley.

Then I switched to a more mobx-like implementation. I liked the idea of having actions as simple methods on the store.

So in version 0.2.0 I did a rewrite: Instead of working with actions as objects like in redux, we now add actions as methods to our store implementations.

Why another store solution?

At the moment I write some applications in Angular and React.

  • There is the @ngrx/store or redux which allows you to have a centralized application state.

    This is a nice solution for a lot of applications. With the great redux dev tools, you can keep track of your actions.

  • On the other hand there is mobx which is a distributed store and works with it's own observable implementation.

Advantages

  • In @ngrx/store and redux you have to create a lot of boilerplate code to finally get your store set-up and you have to jump through a lot of files when you want to change a behavior.

    With the RxStore you have everything from the actions to to the state within one file (like in mobx).

  • Mobx is larger than this small library and it has it's own observable implementation which is unlikely to be reused by a library consumer.

    Since this library uses rxjs, it has a very small size (it's only one class). And rxjs is very light, too (when you include only the operators you need into your application). If you use Angular, you have rxjs as an implicit dependency anyways.

Drawbacks

  • Hot-Module-Reloading might be easier to be implemented with a central store solution.
  • @ngrx/store and redux have an action history log and you can simply modify it with the dev tools to get a different resulting state. This is not a feature of mobx or this store.

API

RxStore

constructor

Creates a new store instance.

new RxStore<S>(initialState: S, options?: RxStoreOptions);

RxStore.prototype.state

This is a reference to the current state snapshot.

RxStore.prototype.state$

The state$ is a rxjs Observable<S>. Here you can subscribe for state updates.

RxStore.prototype.publish(patch: Partial<S>) (protected)

This is an internal method that can be used by your store actions to publish state changes.

RxStoreOptions

debug?: boolean

Determines whether the the state changes of the store are logged to the console.

mutateState?: boolean

Determines whether the state should be mutated when calling publish.

A Todo Example

import { RxStore } from "rx-simple-store";

interface Todo {
  text: string;
  done: boolean;
}

interface TodoState {
  todos: Todo[];
}

class TodoStore extends RxStore<TodoState> {
  addTodo(todo: Todo) {
    this.publish({
      todos: [...this.state.todos, todo],
    });
  }

  updateTodo(idx: number, todo: Partial<Todo>) {
    const todos = this.state.todos;
    this.publish({
      todos: [
        ...todos.slice(0, idx),
        Object.assign(todos[idx], todo),
        ...todos.slice(idx + 1),
      ],
    });
  }
}

const todoStore = new TodoStore({ todos: [] });

todoStore.addTodo({ text: "Read the docs", done: false });
todoStore.updateTodo(0, { done: true });
todoStore.addTodo({ text: "Use it", done: false });
todoStore.updateTodo(1, { text: "Apply it" });

// state:
// ​​​​​{​​​​​
// ​​​​​  "todos": [​​​​​
// ​​​​​    {​​​​​
// ​​​​​      "text": "Read the docs",​​​​​
// ​​​​​      "done": true​​​​​
// ​​​​​    },​​​​​
// ​​​​​    {​​​​​
// ​​​​​      "text": "Apply it",​​​​​
// ​​​​​      "done": false​​​​​
// ​​​​​    }​
// ​​​​​  ]​​​​​
// ​​​​​}​​​

Usage with angular

In angular you can simply subclass the RxStore and inject it to your components.

Example

Store (similar to example above):

// ...

@Injectable()
export class TodoStore extends RxStore<TodoState> {
  // ...
}

Component:

@Component({
  // ...
})
export class TodosComponent {
  todos$ = this.todoStore.state$.map(state => state.todos);

  constructor(public todoStore: TodoStore) {
  }
}

Usage with react, inferno or preact

If you use react or a react-like library, you can connect a stateful component to a store by using the connect decorator.

Example:

import { connect } from "rx-simple-store";

interface State {
  todos: TodoState;
}

@connect({ todos: todoStore })
export class Todos extends Component<{}, State> {
  render() {
    const todos = this.state.todos;
    return (
      <ul>
        {todos.todos.map(todo => (<li>{todo.text}</li>))}
      </ul>
    );
  }
}

About

A simple rx store solution written in TypeScript

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published