npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

mst-collection

v0.5.1

Published

[![npm](https://img.shields.io/npm/v/mst-collection.svg?style=plastic)](https://npmjs.com/package/mst-collection)

Readme

mst-collection

npm

Collection abstraction over mobx-state-tree models.

Installation

Since mst-collection is just a wrapper around mobx-state-tree, we need to make sure we install them all.

npm add mst-collection mobx-state-tree mobx

Usage

Using classes to define models

MST-Collections promotes the way to define models using regular es6 classes which gets rid of this/self mix in views/actions. Also, it's just easier to read and write and works well with TypeScript.

In order to define a model, we need to use Model function:

import { types } from 'mobx-state-tree';
import { Model } from 'mst-collection';

const BaseTodo = Model({
  // here we can pass any props you would pass to types.model({})
  title: types.string,
  isCompleted: false,
});

The Model works pretty much the same as types.model but doesn't return the action model. Instead it returns the base class which we can extend with our actions/views:

class Todo extends BaseTodo {
  // all the actions/views can be defined here
}

In order to avoid some of the naming collisions we can get rid of BaseTodo and run that fabric directly:

class Todo extends Model({
  // here we can pass any props you would pass to types.model({})
  title: types.string,
  isCompleted: false,
}) {
  get info() {
    // we no longer need self
    return `${this.title} - Completed: ${this.isCompleted}`;
  }

  printInfo() {
    // `this` has access to both model and class properties
    console.log(this.info);
  }
}

But still this Todo class is not a MST model. In order to convert it - we should use model function and then we can use it as a regular model:

import { model } from 'mst-collection';

const TodoModel = model(Todo);

const todo = TodoModel.create({
  title: 'Create docs for mst-collection',
});

todo.printInfo(); // prints: Create docs for mst-collection - Completed: false

Adding actions

All the methods in the model class are actions. They should be defined ES6 class methods. Arrow functions won't work (see Limitations sections). Also, they are bound to the model context under the hood se no need for bind in the constructor.

class Todo extends Model({
  // ...
}) {
  toggleCompleted() {
    // this is going to be executed in action context
    // you can synchronously mutate model's props and run other actions
    this.isCompleted = !this.isCompleted;
  }
}

Actions can be async, too. You can mark them as with async and use await in the body of the method. But you can't mutate the props of the model in the async actions, only using other actions:

class Todo extends Model({
  // ...
  id: types.optional(types.number, uuid),
  toggleCompletedStatus: types.enum(['pending', 'running', 'error']),
}) {
  setToggleCompletedStatus(status: 'pending' | 'running' | 'error') {
    this.toggleCompletedStatus = status;
  }

  setIsCompleted(value: boolean) {
    this.isCompleted = value;
  }

  async toggleCompleted() {
    const prevValue = this.isCompleted;

    try {
      this.setToggleCompletedStatus('running');

      this.setIsCompleted(!prevValue);

      await Api.Todo.setCompleted(this.id);

      this.setToggleCompletedStatus('pending');
    } catch (err) {
      this.setToggleCompletedStatus('error');

      this.setIsCompleted(prevValue);
    }
  }
}

Adding views

Views in MST are getters which access some observables and return some value. It works in the same way in our class-based models.

In order to define a view, we need to define a getter:

class Todo extends Model({
  // here we can pass any props you would pass to types.model({})
  title: types.string,
  isCompleted: false,
}) {
  // this is view/computed property
  get info() {
    // we no longer need self
    return `${this.title} - Completed: ${this.isCompleted}`;
  }

  // this will be an ACTION
  // you can use it too but it won't be computed and memoized
  info() {
    // we no longer need self
    return `${this.title} - Completed: ${this.isCompleted}`;
  }
}

Adding views with arguments

Views in mobx-state-tree should be getters in order to apply memoization. But getters don't accept arguments. As a workaround, we use computedFn from mobx-utils. In order to mark some method as view - use @view decorator. It's got its own limitations - refer to mobx-utils for more info.

import { view, Model, model } from 'mst-collection';

class Counter extends Model({
  count: 1,
}) {
  // notice @view decorator is used before the method
  // we can't use default parameters here
  // also, number of params should be the same each time
  @view getCountPlus(value: number) {
    return this.count + value;
  }
}

const CounterModel = model(Counter);

Adding volatile

All the instance properties of the model are volatile.

class Counter extends Model({
  count: 1,
}) {
  // volatile field
  countOfIncrements = 0;

  // this is an action
  increment() {
    // this is observable
    this.count += 1;

    this.incrementCountOfIncrements();
  }

  // this is not an actions
  // you can't mutate model props here
  incrementCountOfIncrements = () => {
    // this is just a local state field
    // so we can mutate it
    this.countOfIncrements += 1;
  };
}

Extending models

TODO

Main Concepts and architecture

TODO: Write about entities, collections, entity refs and why we use them, how we name our models, what is the folder structure and how we inject them into our react components.

RootModel

TODO

CollectionModel

TODO

EntitiesModel

TODO

ListModel

TODO

entityRef

TODO

Thunks

TODO

Using models inside React components

TODO

TODOs

  • [ ] - Add persist
  • [x] - Add ListModel
  • [ ] - Add second param to the model function to specify decorators
  • [ ] - Add ability to config the ref id generator function
  • [ ] - Add ability to change thunk model (add new stuff)
  • [ ] - Add more tests
  • [x] - Collection merge strategy

Limitations

  1. Volatile function don't have access to the props of the model.
class Counter extends Model({
  count: types.number,
}) {
  // this is not a action but volatile function
  getCount = () => {
    // this here doesn't have Model properties
    // so count is undefined
    console.log(this.count);
  };

  // this is an action and it hac access to context
  getCountAction() {
    // logs value of count
    console.log(this.count);
  }
}

const CounterModel = model(Counter);

Acknowledging

This project is inspired by mobx-keystone by @xaviergonz, mst-decorators by @farwayer and classy-mst by @charto.

Special thanks to @mweststrate and the team behind mobx for mobx and mobx-state-tree!

Also, big thanks to Apiko and LetApp where all those ideas have been born, implemented, and used by many projects.

LICENSE

MIT