Skip to content
/ reget Public

React reactive cache for any async calls, http fetching, or memory store accesses

License

Notifications You must be signed in to change notification settings

ericfong/reget

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Reget https://img.shields.io/npm/v/reget.svg state npm npm

Deprecated and please migrate to datavan, which provide more features

HTTP API Accessor for React with auto-reactive-cache and middlewares. Restful React reactive cache for any async calls, http fetching, or memory store accesses.

Features

  • auto-reactive-cache

    • API results are cached and auto prune
    • AutoRunner(function) auto watch and re-run when related caches changed
    • reget.get() is synchronized function, which get cache and trigger http fetch as a side effect
  • middlewares

    • koa middleware like
    • can use middlewares to convert data, as ORM or distribute to diff cache keys
    • also work for synchronized access or localStorage or memory

How It works?

reget is simple key-value cache. During data getting, reget will call a list of middlewares if cache is missing or too old. Result from middlewares will put into the cache and notify all listeners.

You can implement koa-like middlewares to asynchronously fetch HTTP RESTful resources, worker communication or synchronously access localStorage.

Welcome to extend or hack Reget or other classes to change behaviours

Table of Contents

Http get, put, post for react component

import {connectReget} from 'reget'

function PureComponent({user}) {
  return user.name
}

export default connectReget(({userId, reget}) => {
  // assume you have setup middleware to fetch HTTP
  const user = reget.get(`user/${userId}`)
  // first get will be undefined
  // after HTTP response and cached, connectReget will be re-run
  // so, second get will get user
  return {user: user}

  // you can return null to stop child component rendering
})(PureComponent)

Setup

import {Reget, RegetProvider, compose} from 'reget'

// create reget cache and assign it with middlewares
const reget = new Reget({
  // create koa-like middlewares
  handler: compose(async ctx => {
    // fetch http based on ctx.url, body and headers
    ctx.body = await window.fetch(ctx.url, {
      body: ctx.input,
      headers: ctx.headers,
    })
  }),
})

// Assign to your React context
<RegetProvider reget={reget}>
  <MyApp />
</RegetProvider>

Comparison

Compare to Flux/Redux

  • Built-in support for async, http, promise call
  • Designed for big state tree. Cached state will be garbage collected
  • No need to define constants (you can define url generate functions)

Compare to Relay/Falcor

  • Can call any http endpoint (Restful or php page or even image)
  • Flexible code your middleware, data conversion and normalization in js side

Server Rendering

reget.serverRender(reget => {
  return ReactDOMServer.renderToString(<RegetProvider reget={reget}><App /></RegetProvider>)
})
.then(outputHtml => {
  console.log(outputHtml)
})

// transfer data to client
const json = JSON.stringify(reget.getCache())
// -------
const isoData = JSON.parse(json)

// client side
const browserReget = new Reget({
  // handler()
})
browserReget.setCache(isoData)
ReactDOM.render(<RegetProvider reget={browserReget}><App /></RegetProvider>, dom)

You can use iso to ship data to browser

Use Reget alone

Reget can be a

import {Reget, compose, mount} from 'reget'

import browserMiddleware from 'reget/lib/middlewares/browser'
import cacheMiddleware from 'reget/lib/middlewares/cache'
import localStorageMiddleware from 'reget/lib/middlewares/localStorage'
import cookieMiddleware from 'reget/lib/middlewares/cookie'
import koaCookieMiddleware from 'reget/lib/middlewares/koaCookie'


const reget = new Reget({
  handler: compose(
    mount('memory', cacheMiddleware()),
    // or
    {mount: 'memory', handler: cacheMiddleware()},

    {
      route: 'abc/:key',
      async get(ctx, next) {
        // ...
      },
      async put(ctx, next) {
        // ...
      },
      async post(ctx, next) {
        // ...
      },
    }
  ),
})

reget.put('memory/foo', 'bar')
reget.get('memory/foo') === 'bar'

API

reget will create a CallContext instance and pass down to middlewares

CallContext fields, getters and setters

// ctx fields
reget: [Object] // caller reget object. Middleware is valid to call middlewares stack again, (prevent recursive problem on your own).
cache: [Object] // reget's cache. middleware can use this to safely put data into cache

// request related fields
method: 'GET' // default 'GET'
url: '/' // url with query string, default '/'
path: '/' // url without query string, default '/'
ifModifiedSince: null // default null
headers: null // default undefined
input: null // request body or data, default null

// response related fields
status: 404 // http-like response status getter and setter, default 404)
body: null  // http-like response body getter and setter, default null)
get: function() {} // get normalized header
set: function() {} // set header

CacheStore class methods

get(key)  // sync get cache
set(key, value)  // sync set cache, trigger watchers to re-run in next tick.
invalidate(key, allSuffix)  // invalidate cache
watch(key, func)  // register a watcher for change on key
unwatch(key, func)  // unregister a watcher for change on key
hasWatch(key)
getPendingPromise()  // get pending change promise (or null), so you can wait for
prune()  // gc this cache store

Reget class

cache  // this instance's CacheStore
handler()  // assigned handler function for request (GET, PUT, POST), can be created by ```compose``` module
get(pathname, query, option)  // http get (Sync)
put(url, input, option)  // http put (Async/Promise)
post(url, input, option)  // http post (Async/Promise)
reload(url, option)  // http get (Async/Promise)
request(option)  // http request (Async/Promise)
serverRender()
getLoadingPromise(key) // get promise for all loading calls or one cache, null when promise not found
wait()  // wait for all pending requests and events
getCache() // If !key, entire store will be returned
setCache() // If key is object, key&value will be import to this cache store.
invalidate(key, allSuffix)
watch(key, func)
unwatch(key, func)

Middlewares

koa like middlewares system. Please refer to path-to-regexp for path pattern.

import {compose} from 'reget'

const handler = compose(
  {
    route: 'foo/:key',
    async get(ctx, next) {
      // ctx.params = {key: 'hi'}
      await next()
      ctx.body = ctx.body + ' World'
    },
    async put(ctx, next) {
      DB.save(ctx.input)
      ctx.status = 200
    },
  },
  ctx => {
    ctx.body = 'Hello'
  },
)

const ctx = {path: 'foo/hi'}
await handler(ctx)
// ctx.body === 'Hello World'

Middleware API

compose(...middlewares) create a function that accept a context argument and pass down to all middlewares

middlewares can be one of

  • function(ctx, next){}
  • array of function(ctx, next){}
  • object: {route: 'pathPattern', get(){}, put(){}, post(){}, watch(){}, unwatch(){}}
  • object: {mount: 'prefix', handler: anotherMiddleware}
  • mount('prefix', anotherMiddleware)

like koa-route, path pattern use path-to-regexp to build regexp

Middleware Example

browserMiddleware = {
  route: '/:key',
  watch({params: {key}, mountPath, reget}) {
    if (key === 'height' || key === 'width') {
      window.onresize = function() {
        const changes = {
          // mountPath = 'browser' if you use mount('browser', browserMiddleware)
          [`${mountPath}/height`]: window.innerHeight,
          [`${mountPath}/width`]: window.innerWidth,
        }
        reget.setCache(changes)
      }
      window.onresize()
    }
  },
  unwatch(ctx) {
    const {key} = ctx.params
    if (key === 'height' || key === 'width') {
      window.onresize = null
    }
  },
  get(ctx) {
    // just use the cached value
    ctx.status = 304
  },
  put(ctx) {
    // cannot put
    ctx.status = 404
  },
}

SyncPromise

SyncPromise can wrap a value into a Promise like object. Why? because normal Promise .then is not sync

let value = null

Promise.resolve('A')
.then(val => value = val)
// value !== 'A'

// But

SyncPromise.resolve('A')
.then(val => value = val)
// value === 'A'

About

React reactive cache for any async calls, http fetching, or memory store accesses

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published