Skip to content

A library to manage application state, heavily inspired by mobx-state-tree

Notifications You must be signed in to change notification settings

ForsakenHarmony/parket

Repository files navigation



  • Small (~1.5KB)
  • Immutable from the outside, mutable in actions
  • Reactive (state emits updates without explicit calls to i.e. setState)
  • Modular (you can nest models inside each other)

Why?

I was disappointed with all the current state management solutions. Then I found mobx-state-tree, which seemed like a godsend to me (ok not really, but I liked the concept), but it was pretty big in terms of file size (mobx alone is big: 16.5kB). So I thought it's surely possible to make a smaller version of it, that's how this started. And after 2 failed attempts I finally got something that works well

Installation

$ npm i parket
// ES6
import { model } from 'parket';
// CJS
const { model } = require('parket');

Usage

Note: This library uses Proxies and Symbols. Proxies cannot be fully polyfilled so you have to target modern browers which support Proxies.

Basic example

import { model } from 'parket';
// model returns a "constructor" function
const Person = model('Person', {
  // name is used internally for serialization
  initial: () => ({
    firstname: null,
    lastname: null,
    nested: null,
  }),
  actions: (state) => ({
    setFirstName(first) {
      state.firstname = first; // no set state, no returns to merge, it's reactive™
    },
    setLastName(last) {
      state.lastname = last;
    },
    setNested(nested) {
      state.nested = nested;
    },
  }),
  views: (state) => ({
    fullname: () => `${state.firstname} ${state.lastname}`, // views are computed properties
  }),
});

// merge an object with the initial state
const instance = Person({ firstname: 'Tom' });

// you can subscribe to actions, patches (state updates) and snapshots (full state after actions)
const unsubscribe = instance.onSnapshot(console.log);

// you can unsubscribe by calling the function returned by the listener
// unsubscribe();

instance.setLastName('Clancy');

// views turn into cached getters
console.log(instance.fullname); // 'Tom Clancy'

// nested models also bubble up events to the parent
instance.setNested(Person());

instance.nested.setFirstName('wow');

// you can get a snapshot of the state at any time
// { firstname: 'Tom', lastname: 'Clancy',  nested: { firstname: 'wow', lastname: null, nested: null } }
console.log(instance.getSnapshot());

Async example

const Async = model('Async', {
  initial: () => ({
    loading: false,
    result: null,
  }),
  actions: (self) => ({
    async doSomethingAsync() {
      // actions can be async, parket doesn't care
      self.loading = true;
      self.result = await somethingAsync(); // be aware that you should handle errors
      self.loading = false;
    },
  }),
});

preact / react

import { Component } from 'preact';
import { observe, connect, Provider } from 'parket/preact'; // or 'parket/react'

// observe keeps the component updated to models in the prop
@observe
class Observed extends Component {
  render({ person }) {
    // if you're using react, props don't get passed to render so you have to use `const {person} = this.props;`
    return (
      <div>
        <h1>{person.fullname}</h1>
      </div>
    );
  }
}

// connect inserts the store/instance into props
@connect
class Person extends Component {
  render({ store }) {
    // if you're using react, props don't get passed to render so you have to use `const {store} = this.props;`
    return (
      <div>
        <h1>{store.fullname}</h1>
      </div>
    );
  }
}

// Provider adds an instance to the context
const root = () => (
  <Provider store={instance}>
    <div id="app">
      <Person />
      <Observed person={instance} />
    </div>
  </Provider>
);

preact / react hooks

function Observed({ person }) {
  useObserved(person);

  return (
    <div>
      <h1>{person.fullname}</h1>
    </div>
  );
}

function Person() {
  const store = useStore();

  return (
    <div>
      <h1>{store.fullname}</h1>
    </div>
  );
}

// Provider adds an instance to the context
const root = () => (
  <Provider store={instance}>
    <div id="app">
      <Person />
      <Observed person={instance} />
    </div>
  </Provider>
);

Credits

License

MIT © hrmny.sh

About

A library to manage application state, heavily inspired by mobx-state-tree

Resources

Code of conduct

Stars

Watchers

Forks

Packages

No packages published