• This repository has been archived on 10/Dec/2017
  • Stars
    star
    281
  • Rank 147,023 (Top 3 %)
  • Language
    JavaScript
  • License
    MIT License
  • Created over 8 years ago
  • Updated about 8 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A data store with declarative querying, observable state, and easy undo/redo.

mobx-store

CircleCI npm Coveralls

A data store with declarative querying, observable state, and easy undo/redo.

Why

Query your data declaratively like it is SQL

import mobxstore from 'mobx-store'
import { filter, map, pick, sortBy, take } from 'lodash/fp'

// Create store
const store = mobxstore({ users: [] })

// SELECT name, age FROM users WHERE age > 18 ORDER BY age LIMIT 1
store('users', [map(pick(['name', 'age'])), filter((x) => x.age > 18), sortBy('age'), take(1)])

Schedule reactions to state changes

import mobxstore from 'mobx-store'
import { filter } from 'lodash/fp'

function log(store) {
  console.log(store('numbers', filter((x) => x > 10)))
}

// Create empty store
const store = mobxstore({ numbers: [] })

// Schedule log so that it happens every time the store mutates
store.schedule([log, store])

// log is invoked on the push because the store mutated
store('numbers').push(1)
/*
  logs [] because 1 < 10
*/

// log is invoked on the push because the store mutated
store('numbers').push(12)
/*
  logs [12]
*/

Easy undo and redo

store('test').push(1, 2, 3) // value of test is [1, 2, 3]
store.undo('test') // value of test is [] again
store.redo('test') // value of test is [1, 2, 3] again

Easy interop with React

One of the best things about the store is that you can use it with mobx-react because it's based upon MobX. This also means that when you mutate your objects you don't need setState() calls because MobX will handle all the updating for you.

import React from 'react'
import mobxstore from 'mobx-store'
import { observer } from 'mobx-react'

const store = mobxstore({ objects: [] })

const Objects = observer(function() {
  function addCard() {
    store('objects').push({ name: 'test' })
  }
  return (
    <div>
      <button onClick={addCard}>Add New Card</button>
      <div>
        {store('objects').map((o, n) =>
          <div key={n}>
            {o.name}
          </div>
        )}
      </div>
    </div>
  )
})

export default Objects

Example

Here's a quick demo I put together to demonstrate the observable state and undo/redo features. It uses the code you can find later in the README to make changes to the store automatically persist to localstorage.

Installation

npm install --save mobx-store

Keeping your bundle small

If you're concerned about the extra weight that lodash will add to your bundle you can install babel-plugin-lodash

npm install --save-dev babel-plugin-lodash

and add it to your .babelrc

{
  "presets": // es2015, stage-whatever
  "plugins": [/* other plugins */, "lodash"]
}

this way you can do modular imports, and reduce the size of your bundles on the frontend

import { map, take, sortBy } from 'lodash/fp'

Tutorial

The store is structured as an object that holds either an array or object for each key. For example, something like

{
  numbers: [],
  ui: {}
}

To create a store all you need to do is

import mobxstore from 'mobx-store'

// Create empty store and initialize later
const store = mobxstore()
store.set('users', [])

// Create store with initial state
const store = mobxstore({
  users: [{ name: 'joe', id: 1 }]
})

and to get access to specific key such as users you would just call.

store('users')

With arrays you can manipulate them as if they are native arrays, but if you made an object you interact with it using the get and set methods

store('ui').get('isVisible')
store('ui').set('isVisible', true)

Reading from and writing to the store

mobx-store has a simple lodash powered API.

  • Reading from the store is as simple as passing lodash methods to the store function. In order to pass methods to the store without actually executing them you can import from lodash/fp.

  • Writing to the store is done by calling the regular array methods as well the methods MobX exposes such as replace on the store object.

import { filter } from 'lodash/fp'
import mobxstore from 'mobx-store'

const store = mobxstore({ numbers: [] })

store('numbers') // read current value of store -- []
store('numbers').replace([1, 2, 3]) // write [1, 2, 3] to store
store('numbers').push(4) // push 4 into the store
store('numbers', filter((v) => v > 1)) // read [2, 3, 4] from store

You can also chain methods to create more complex queries by passing an array of functions to the store.

import { filter, map, sortBy, take, toUpper } from 'lodash/fp'
import mobxstore from 'mobx-store'

const store = mobxstore({ users: [] })

// Sort users by id and return an array of those with ids > 20
const result = store('users', [sortBy('id'), filter((x) => x.id > 20)])

If you save the result of one of your queries to a variable, you can continue working with the variable by using the chain API

// Take the top 3, and return an array of their names
store.chain(result, [take(3), map('name')])

// Filter again to get those with ids less than 100, take the top 2, and return an array of their names capitalized
store.chain(result, [filter((x) => x.id < 100), take(2), map((v) => toUpper(v.name))])

Scheduling reactions to state change

Reacting to state changes is done through the schedule API. You pass one to many arrays to the function. The first element of the array is your function, and the following elements are the arguments of your array.

For example mobx-store comes with an adapter for reading and writing to localstorage, which looks like this.

function read(source) {
  const data = localStorage.getItem(source)
  if (data) {
    return JSON.parse(data)
  }
  return {}
}

function write(dest, obj) {
  return localStorage.setItem(dest, JSON.stringify(obj))
}

export default { read, write }

Using this we can schedule writing to the localstorage whenever the store mutates.

import mobxstore from 'mobx-store'
import localstorage from 'mobx-store/localstorage'

// Create store initialized with value of localstorage at "info"
const store = mobxstore(localstorage.read('info'))

// schedule a reaction to changes to the state of the store
store.schedule([localstorage.write, 'info', store])

and you're done. Every change you make to this instance of mobx-store will persist to localstorage.

Undo and redo

To use undo and redo pass the name of a key in your store as a parameter. Make sure not to undo if you haven't altered the state of your store, or if you have called it too many times already, and likewise make sure not to call redo if you haven't yet called undo.

import mobxstore from 'mobx-store'

const store = mobxstore({ x: [] })

store.undo('x') // error

store('x').push(1)
store.undo('x') // undo push
store.redo('x') // redo push

store.redo('x') // error

You can avoid errors by using the functions canRedo and canUndo

if (store.canUndo('x')) {
  store.undo('x')
}
if (store.canRedo('x')) {
  store.redo('x')
}

You can limit the history of the undo by passing limitHistory to the store config

// Can only undo up to 10 times
const store = mobxstore({}, { limitHistory: 10 })

Limiting history should be usually be unnecessary as mobx-store doesn't store the entire object in history like Redux does, which potentially can take up a lot of memory. Instead, it only stores information about what changed, and only creates the new state when you call undo or redo.

Using with React

Read and apply the instructions you can find at mobx-react to make your components update when your store updates. The gist of it is that you just

import { observer } from 'mobx-react'

and wrap the component that is using your store in it.

Credit

More Repositories

1

WebpackTutorial

A simple webpack tutorial
JavaScript
2,232
star
2

csv-parser

Fast, header-only, extensively tested, C++11 CSV parser
C++
140
star
3

aria.ai

My Personal Website: Click it --->>>
TypeScript
19
star
4

ppx_str

PPX for template strings
OCaml
14
star
5

leetcode

Answers to interview questions
C++
8
star
6

ocaml-str-concat-benchmark

Determining which way of concatenating strings is fastest
OCaml
8
star
7

crescendo

🎹
Python
5
star
8

formulagrid

Formula Database
JavaScript
4
star
9

weak-memoize

Self-invalidating memoization for async functions
JavaScript
4
star
10

napjs

CLI Alarm Clock
JavaScript
3
star
11

newtab

Chrome extension that lets you embed any website into your new tab page
CSS
2
star
12

twitter-reactions

Chrome extension that added slack-like reactions to twitter
JavaScript
2
star
13

imgur-bot

A reddit bot that would use the reddit API to find Gyazo links and would reupload them to Imgur using their API.
Python
2
star
14

steam-api-example

Quick example using the steam api
JavaScript
2
star
15

eslint-config-aria-react

React eslint configuration
JavaScript
1
star
16

aoc2023

OCaml
1
star
17

babel-preset-node-flow

JavaScript
1
star
18

smalldatamatters

Let small data dream big
HTML
1
star
19

.emacs.d

Emacs Config
Emacs Lisp
1
star
20

bad-snake

why did I use react? also it's broken anyways
JavaScript
1
star
21

s2r

bad
JavaScript
1
star
22

eslint-config-aria

Personal eslint config
JavaScript
1
star
23

aspect

πŸ‘€
JavaScript
1
star
24

love-snake

Snake written in Lua with LΓ–VE 2d engine
Lua
1
star
25

babel-preset-aria

My babel preset
JavaScript
1
star
26

pwrap

Add promise capability to any function.
JavaScript
1
star
27

redditcss

A reddit css theme
CSS
1
star
28

blog

Personal Blog
1
star
29

haskell

Learning Haskell 🏫
Haskell
1
star