4.2.8 • Published 6 years ago

datavan v4.2.8

Weekly downloads
7
License
MIT
Repository
github
Last release
6 years ago

datavan: in-memory mongodb for react that can track changes and sync with server

https://img.shields.io/npm/v/datavan.svg state npm npm

Features

  • based on New React Context
  • mongodb-like find(), update() api
  • design with offline in mind
  • customizable server fetch, submit, persistent, conflict-resolve logic
  • built-in reselect-like memoizing layer
  • supports server rendering
  • code in es6, support tree-shaking

How It works?

During find(), datavan will query your local-data first. If local-data is missing, it will call your onFetch() as a side effect and update the local-data.

Table of Contents

Other Docs

Getting Started

import _ from 'lodash'
import { createDatavanContext } from 'datavan'

const Van = createDatavanContext({
  // defined collection called 'myUserTable'
  myUserTable: {
    onFetch(collection, query, option) {
      return Promise.resolve([{ _id: 'id', name: 'john' }])
    },
  },
})

render(
  <Van.Provider store={store}>
    ...
    <Van>
      {db => {
        // first call result will be undefined
        // after HTTP response, connect will be re-run
        // second result will get user object
        const users = db.find('myUserTable', { name: { $in: ['John'] } })

        const onClick = () => db.update('myUserTable', { name: 'John' }, { $merge: { name: 'smith' } })

        return (
          <button onClick={onClick}>
            {_.map(users, 'name').join()}
          </button>
        )
      }}
    </Van>
    ...
  </Van.Provider>
)

Setup

createDb

import { createDb } from 'datavan'

const db = createDb({
  myUserTable: {
    // id field for document (default: `_id`)
    idField: 'id',

    // async fetch function (default: `undefined`). Should return array or map of documents
    onFetch(query, option, collection) {
      return fetch('restful-api?name=john')
    },

    // cast and convert doc fields. Return: casted doc
    // NOTE only cast to primitive types that can use === to compare. (DON'T cast to Date, Object, Array or anything that cannot be JSON.stringify)
    cast(doc) {
      doc.count = parseInt(doc.count, 10)
      // Can cast to Number as count === JSON.parse(JSON.stringify(count))
      doc.arr = 'a,b'.split(',')
      // Can cast to Number as arr !== JSON.parse(JSON.stringify(arr))
      return doc
    },

    onInsert(doc) {
    },

    onLoad(doc) {
    },

    // generate a new tmp id string (default: genId from datavan)
    genId: genId,

    getFetchQuery: (query, idField) => '',
    // calculate and return fetchKey (to determine cache hit or miss) from fetchQuery (default: defaultGetQueryString from datavan)
    getFetchKey: (query, option) => '',

    // another way to setup initial data. With `{ byId: {}, originals: {}, fetchAts: {} }` object tables.
    initState: {
      // byId is table of docs
      byId: {
        'user-1': { _id: 'user-1', name: 'John' },
      },
      // originals is table of modified docs' originals
      originals: {
        'user-1': { _id: 'user-1', name: 'Old Name' },
      },
      // fetchAts is server fetched queries times (msec, to prevent re-fetch after server rendering)
      fetchAts: {},
    },
  },
})

db.find('myUserTable', { name: 'John' })

data inside db

datavan will store docs in the following structure

const db = createDb()
db = {
  user_table: {
    submits: {
      id_1: {
        _id: 'id_1',
        name: 'John',
      },
      id_2: {
        _id: 'id_2',
        name: 'May',
      },
    },

    // table of modified docs' originals
    originals: {},
    preloads: {},

    // server fetch times/markers (msec, to prevent re-fetch after server rendering)
    fetchAts: {},
  },
}

Better Performance

memoize result and auto re-run

import { createDatavanContext, getMemoizeHoc } from 'datavan'

const Van = createDatavanContext(...)
const withVan = getMemoizeHoc(Van)

const MyApp = withVan(
  // array of props that provide to map function
  ['name', 'role'],
  // map function
  (db, { name, role }) => {
    return { users: db.find('user_table', { name, role }) }
  }
)(ReactComponent)

API

find, pick

db.find(collection, query, [option])
// Return: Array of documents
  • collection: collection name
  • query: Array | query-object (mongodb like query object, we use mingo to filter documents)
arr = db.find('user_table', { name: 'john' })

// query starts with $$ which trigger find within the result from onFetch response
arr = db.find('user_table', { name: 'john', $$limit: 10, $$sort: ... })

userById = db.pick('user_table', { name: 'john' })

findAsync, pickAsync

db.findAsync(collection, query, [option])

byId = db.pickAsync(collection, query, [option])

Async function that always fetch and find data from server

findInMemory

like find() but only find in local memory

fetch

internally used by find(), findAsync() to call onFetch and return a raw result in promise. Without call findInMemory() after onFetch to normalise onFetch result.

get

doc = db.get('user_table', 'id-123')

getById, getOriginals, getSubmits

// get all documents. This won't trigger onFetch()
const docsTable = db.getById('user_table')

// get local changed documents
const dirtyDocs = db.getSubmits('user_table')

// get local changed documents' originals
const originalDocs = db.getOriginals('user_table')

const docsPreloads = db.getPreloads('user_table')

insert

Return: inserted docs

insertedDoc = db.insert('user_table', { name: 'Mary' })
// can also insert array
insertedDocs = db.insert('user_table', [{ name: 'Mary' }, { name: 'John' }])

update

const query = { name: 'Mary' }
const mutation = { $merge: { name: 'Mary C' } }
db.update('user_table', query, mutation)

remove

remove all docs that match the query

db.remove('user_table', { name: 'May' })

mutate

mutate documents using immutability-helper syntax

// merge by doc id
db.mutate('user_table', 'id-123', { $merge: { name: 'Mary' } })

// merge by array of path
db.mutate('user_table', ['id-123', 'name'], { $set: 'Mary' })

// merge in many docs
db.mutate('user_table', { $merge: { docId1: doc1, docId2: doc2 } })

set

shortcut of mutate which always use { $set: value }

invalidate

// invalidate all collections
db.invalidate()

// invalidate one collection (all docs)
db.invalidate('user_table')

// invalidate one collection (some docs)
db.invalidate('user_table', ['user-1', 'user-2'])

reset

reset local change and re-fetch in future get/find

// reset all collections
db.reset()

// reset one collection (all docs)
db.reset('user_table')

// reset one collection (some docs)
db.reset('user_table', ['user-1', 'user-2'])

load

load bulk data into store. data can be

db.load('user_table', data, option)
  • Array of docs
  • Or a object with { byId: {}, originals: {}, fetchAts: {} }
  • Or Table of docs
// Array of docs
db.load('user_table', [{ _id: 'user-1', name: 'John' }])

// Or a object with at least one of `{ byId: {}, originals: {}, fetchAts: {} }`
db.load('user_table', {
  // byId is table of docs
  byId: {
    'user-1': { _id: 'user-1', name: 'John' },
  },
  // originals is table of modified docs' originals
  originals: {
    'user-1': { _id: 'user-1', name: 'Old Name' },
  },
  // fetchAts is server fetched queries times (msec, to prevent re-fetch after server rendering)
  fetchAts: {},
  // submitted tmp and stored id mapping
  $submittedIds: { tmpId: storedId },
})

// Or Table of docs (byId)
db.load('user_table', {
  'user-1': { _id: 'user-1', name: 'John' },
})

// load collections
db.load({
  'user_table': {
    'user-1': { _id: 'user-1', name: 'John' },
  },
  'collection-2': {...}
})
  • load() data will consider as fill data from backend and trigger re-render

recall

call a function (anonymous or collection-defined) only-if store data or argument changed. If no changes, cached result will be used.

// recall collection-defined function
const db = createDb({
  myUserTable: {
    groupByFunc(byId, arg1) {
      return _.groupBy(byId, arg1)
    },
  },
})
const result = db.recall('myUserTable', 'groupByFunc', 'arg1-value')
4.2.8

6 years ago

4.2.7

6 years ago

4.2.6

6 years ago

4.2.5

6 years ago

4.2.4

6 years ago

4.2.3

6 years ago

4.2.2

6 years ago

4.2.1

6 years ago

4.2.0

6 years ago

4.2.0-0

6 years ago

4.1.5

6 years ago

4.1.3

6 years ago

4.1.2

6 years ago

4.1.0

6 years ago

4.0.0

6 years ago

4.0.0-8

6 years ago

4.0.0-7

6 years ago

4.0.0-6

6 years ago

4.0.0-4

6 years ago

4.0.0-3

6 years ago

4.0.0-2

6 years ago

4.0.0-1

6 years ago

4.0.0-0

6 years ago

3.5.6

6 years ago

3.5.5

6 years ago

3.5.4

6 years ago

3.5.3

6 years ago

3.5.2

6 years ago

3.5.1

6 years ago

3.5.0

6 years ago

3.4.5

6 years ago

3.4.4

6 years ago

3.4.3

6 years ago

3.4.1

6 years ago

3.4.0

6 years ago

3.3.8

6 years ago

3.3.7

6 years ago

3.3.6

6 years ago

3.3.5

6 years ago

3.3.4

6 years ago

3.3.3

6 years ago

3.3.1

6 years ago

3.3.0

6 years ago

3.2.0

6 years ago

3.1.0

6 years ago

3.0.2

6 years ago

3.0.1

6 years ago

3.0.0

6 years ago

2.11.1

6 years ago

2.11.0

6 years ago

2.10.4

6 years ago

2.10.3

6 years ago

2.10.2

6 years ago

2.10.1

6 years ago

2.10.0

6 years ago

2.9.1

6 years ago

2.9.0

6 years ago

2.8.0

6 years ago

2.7.3

6 years ago

2.7.2

6 years ago

2.7.1

6 years ago

2.7.0

6 years ago

2.6.8

6 years ago

2.6.7

6 years ago

2.6.6

7 years ago

2.6.5

7 years ago

2.6.4

7 years ago

2.6.3

7 years ago

2.6.2

7 years ago

2.6.1

7 years ago

2.6.0

7 years ago

2.5.5

7 years ago

2.5.4

7 years ago

2.5.3

7 years ago

2.5.2

7 years ago

2.5.1

7 years ago

2.5.0

7 years ago

2.4.1

7 years ago

2.3.6

7 years ago

2.4.0

7 years ago

2.3.5

7 years ago

2.3.4

7 years ago

2.3.3

7 years ago

2.3.2

7 years ago

2.3.1

7 years ago

2.3.0

7 years ago

2.2.3

7 years ago

2.2.2

7 years ago

2.2.1

7 years ago

2.2.0

7 years ago

2.1.7

7 years ago

2.1.6

7 years ago

2.1.5

7 years ago

2.1.4

7 years ago

2.1.3

7 years ago

2.1.2

7 years ago

2.1.1

7 years ago

2.1.0

7 years ago

2.0.2

7 years ago

1.11.3

7 years ago

1.11.2

7 years ago

2.0.1

7 years ago

2.0.0

7 years ago

1.11.1

7 years ago

1.11.0

7 years ago

1.10.13

7 years ago

1.10.12

7 years ago

1.10.11

7 years ago

1.10.10

7 years ago

1.10.9

7 years ago

1.10.8

7 years ago

1.10.7

7 years ago

1.10.6

7 years ago

1.10.5

7 years ago

1.10.4

7 years ago

1.10.3

7 years ago

1.10.2

7 years ago

1.10.1

7 years ago

1.10.0

7 years ago

1.9.1

7 years ago

1.9.0

7 years ago

1.8.2

7 years ago

1.8.1

7 years ago

1.8.0

7 years ago

1.7.8

7 years ago

1.7.7

7 years ago

1.7.6

7 years ago

1.7.5

7 years ago

1.7.4

7 years ago

1.7.3

7 years ago

1.7.2

7 years ago

1.7.1

7 years ago

1.7.0

7 years ago

1.6.0

7 years ago

1.5.3

7 years ago

1.5.2

7 years ago

1.5.1

7 years ago

1.5.0

7 years ago

1.4.1

7 years ago

1.4.0

7 years ago

1.3.1

7 years ago

1.3.0

7 years ago

1.1.0

7 years ago

1.0.3

7 years ago

1.0.2

7 years ago

1.0.1

7 years ago

1.0.0

7 years ago