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
- Setup
- Comparison
- Server Rendering
- Use Reget alone
- CacheStore class methods
- Reget class
- Middlewares
- SyncPromise
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)
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>
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
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
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'
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
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
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)
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'
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
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 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'