Comparing version 1.0.0 to 1.1.0
@@ -1,1 +0,1 @@ | ||
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}(g.dva||(g.dva={})).fetch=f()}})(function(){var define,module,exports;return function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s}({1:[function(require,module,exports){require("whatwg-fetch");module.exports=self.fetch.bind(self)},{"whatwg-fetch":2}],2:[function(require,module,exports){(function(self){"use strict";if(self.fetch){return}var support={searchParams:"URLSearchParams"in self,iterable:"Symbol"in self&&"iterator"in Symbol,blob:"FileReader"in self&&"Blob"in self&&function(){try{new Blob;return true}catch(e){return false}}(),formData:"FormData"in self,arrayBuffer:"ArrayBuffer"in self};function normalizeName(name){if(typeof name!=="string"){name=String(name)}if(/[^a-z0-9\-#$%&'*+.\^_`|~]/i.test(name)){throw new TypeError("Invalid character in header field name")}return name.toLowerCase()}function normalizeValue(value){if(typeof value!=="string"){value=String(value)}return value}function iteratorFor(items){var iterator={next:function(){var value=items.shift();return{done:value===undefined,value:value}}};if(support.iterable){iterator[Symbol.iterator]=function(){return iterator}}return iterator}function Headers(headers){this.map={};if(headers instanceof Headers){headers.forEach(function(value,name){this.append(name,value)},this)}else if(headers){Object.getOwnPropertyNames(headers).forEach(function(name){this.append(name,headers[name])},this)}}Headers.prototype.append=function(name,value){name=normalizeName(name);value=normalizeValue(value);var list=this.map[name];if(!list){list=[];this.map[name]=list}list.push(value)};Headers.prototype["delete"]=function(name){delete this.map[normalizeName(name)]};Headers.prototype.get=function(name){var values=this.map[normalizeName(name)];return values?values[0]:null};Headers.prototype.getAll=function(name){return this.map[normalizeName(name)]||[]};Headers.prototype.has=function(name){return this.map.hasOwnProperty(normalizeName(name))};Headers.prototype.set=function(name,value){this.map[normalizeName(name)]=[normalizeValue(value)]};Headers.prototype.forEach=function(callback,thisArg){Object.getOwnPropertyNames(this.map).forEach(function(name){this.map[name].forEach(function(value){callback.call(thisArg,value,name,this)},this)},this)};Headers.prototype.keys=function(){var items=[];this.forEach(function(value,name){items.push(name)});return iteratorFor(items)};Headers.prototype.values=function(){var items=[];this.forEach(function(value){items.push(value)});return iteratorFor(items)};Headers.prototype.entries=function(){var items=[];this.forEach(function(value,name){items.push([name,value])});return iteratorFor(items)};if(support.iterable){Headers.prototype[Symbol.iterator]=Headers.prototype.entries}function consumed(body){if(body.bodyUsed){return Promise.reject(new TypeError("Already read"))}body.bodyUsed=true}function fileReaderReady(reader){return new Promise(function(resolve,reject){reader.onload=function(){resolve(reader.result)};reader.onerror=function(){reject(reader.error)}})}function readBlobAsArrayBuffer(blob){var reader=new FileReader;reader.readAsArrayBuffer(blob);return fileReaderReady(reader)}function readBlobAsText(blob){var reader=new FileReader;reader.readAsText(blob);return fileReaderReady(reader)}function Body(){this.bodyUsed=false;this._initBody=function(body){this._bodyInit=body;if(typeof body==="string"){this._bodyText=body}else if(support.blob&&Blob.prototype.isPrototypeOf(body)){this._bodyBlob=body}else if(support.formData&&FormData.prototype.isPrototypeOf(body)){this._bodyFormData=body}else if(support.searchParams&&URLSearchParams.prototype.isPrototypeOf(body)){this._bodyText=body.toString()}else if(!body){this._bodyText=""}else if(support.arrayBuffer&&ArrayBuffer.prototype.isPrototypeOf(body)){}else{throw new Error("unsupported BodyInit type")}if(!this.headers.get("content-type")){if(typeof body==="string"){this.headers.set("content-type","text/plain;charset=UTF-8")}else if(this._bodyBlob&&this._bodyBlob.type){this.headers.set("content-type",this._bodyBlob.type)}else if(support.searchParams&&URLSearchParams.prototype.isPrototypeOf(body)){this.headers.set("content-type","application/x-www-form-urlencoded;charset=UTF-8")}}};if(support.blob){this.blob=function(){var rejected=consumed(this);if(rejected){return rejected}if(this._bodyBlob){return Promise.resolve(this._bodyBlob)}else if(this._bodyFormData){throw new Error("could not read FormData body as blob")}else{return Promise.resolve(new Blob([this._bodyText]))}};this.arrayBuffer=function(){return this.blob().then(readBlobAsArrayBuffer)};this.text=function(){var rejected=consumed(this);if(rejected){return rejected}if(this._bodyBlob){return readBlobAsText(this._bodyBlob)}else if(this._bodyFormData){throw new Error("could not read FormData body as text")}else{return Promise.resolve(this._bodyText)}}}else{this.text=function(){var rejected=consumed(this);return rejected?rejected:Promise.resolve(this._bodyText)}}if(support.formData){this.formData=function(){return this.text().then(decode)}}this.json=function(){return this.text().then(JSON.parse)};return this}var methods=["DELETE","GET","HEAD","OPTIONS","POST","PUT"];function normalizeMethod(method){var upcased=method.toUpperCase();return methods.indexOf(upcased)>-1?upcased:method}function Request(input,options){options=options||{};var body=options.body;if(Request.prototype.isPrototypeOf(input)){if(input.bodyUsed){throw new TypeError("Already read")}this.url=input.url;this.credentials=input.credentials;if(!options.headers){this.headers=new Headers(input.headers)}this.method=input.method;this.mode=input.mode;if(!body){body=input._bodyInit;input.bodyUsed=true}}else{this.url=input}this.credentials=options.credentials||this.credentials||"omit";if(options.headers||!this.headers){this.headers=new Headers(options.headers)}this.method=normalizeMethod(options.method||this.method||"GET");this.mode=options.mode||this.mode||null;this.referrer=null;if((this.method==="GET"||this.method==="HEAD")&&body){throw new TypeError("Body not allowed for GET or HEAD requests")}this._initBody(body)}Request.prototype.clone=function(){return new Request(this)};function decode(body){var form=new FormData;body.trim().split("&").forEach(function(bytes){if(bytes){var split=bytes.split("=");var name=split.shift().replace(/\+/g," ");var value=split.join("=").replace(/\+/g," ");form.append(decodeURIComponent(name),decodeURIComponent(value))}});return form}function headers(xhr){var head=new Headers;var pairs=(xhr.getAllResponseHeaders()||"").trim().split("\n");pairs.forEach(function(header){var split=header.trim().split(":");var key=split.shift().trim();var value=split.join(":").trim();head.append(key,value)});return head}Body.call(Request.prototype);function Response(bodyInit,options){if(!options){options={}}this.type="default";this.status=options.status;this.ok=this.status>=200&&this.status<300;this.statusText=options.statusText;this.headers=options.headers instanceof Headers?options.headers:new Headers(options.headers);this.url=options.url||"";this._initBody(bodyInit)}Body.call(Response.prototype);Response.prototype.clone=function(){return new Response(this._bodyInit,{status:this.status,statusText:this.statusText,headers:new Headers(this.headers),url:this.url})};Response.error=function(){var response=new Response(null,{status:0,statusText:""});response.type="error";return response};var redirectStatuses=[301,302,303,307,308];Response.redirect=function(url,status){if(redirectStatuses.indexOf(status)===-1){throw new RangeError("Invalid status code")}return new Response(null,{status:status,headers:{location:url}})};self.Headers=Headers;self.Request=Request;self.Response=Response;self.fetch=function(input,init){return new Promise(function(resolve,reject){var request;if(Request.prototype.isPrototypeOf(input)&&!init){request=input}else{request=new Request(input,init)}var xhr=new XMLHttpRequest;function responseURL(){if("responseURL"in xhr){return xhr.responseURL}if(/^X-Request-URL:/m.test(xhr.getAllResponseHeaders())){return xhr.getResponseHeader("X-Request-URL")}return}xhr.onload=function(){var options={status:xhr.status,statusText:xhr.statusText,headers:headers(xhr),url:responseURL()};var body="response"in xhr?xhr.response:xhr.responseText;resolve(new Response(body,options))};xhr.onerror=function(){reject(new TypeError("Network request failed"))};xhr.ontimeout=function(){reject(new TypeError("Network request failed"))};xhr.open(request.method,request.url,true);if(request.credentials==="include"){xhr.withCredentials=true}if("responseType"in xhr&&support.blob){xhr.responseType="blob"}request.headers.forEach(function(value,name){xhr.setRequestHeader(name,value)});xhr.send(typeof request._bodyInit==="undefined"?null:request._bodyInit)})};self.fetch.polyfill=true})(typeof self!=="undefined"?self:this)},{}]},{},[1])(1)}); | ||
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}(g.dva||(g.dva={})).fetch=f()}})(function(){var define,module,exports;return function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s}({1:[function(require,module,exports){require("whatwg-fetch");Object.defineProperty(exports,"__esModule",{value:true});exports.default=self.fetch.bind(self)},{"whatwg-fetch":2}],2:[function(require,module,exports){(function(self){"use strict";if(self.fetch){return}var support={searchParams:"URLSearchParams"in self,iterable:"Symbol"in self&&"iterator"in Symbol,blob:"FileReader"in self&&"Blob"in self&&function(){try{new Blob;return true}catch(e){return false}}(),formData:"FormData"in self,arrayBuffer:"ArrayBuffer"in self};function normalizeName(name){if(typeof name!=="string"){name=String(name)}if(/[^a-z0-9\-#$%&'*+.\^_`|~]/i.test(name)){throw new TypeError("Invalid character in header field name")}return name.toLowerCase()}function normalizeValue(value){if(typeof value!=="string"){value=String(value)}return value}function iteratorFor(items){var iterator={next:function(){var value=items.shift();return{done:value===undefined,value:value}}};if(support.iterable){iterator[Symbol.iterator]=function(){return iterator}}return iterator}function Headers(headers){this.map={};if(headers instanceof Headers){headers.forEach(function(value,name){this.append(name,value)},this)}else if(headers){Object.getOwnPropertyNames(headers).forEach(function(name){this.append(name,headers[name])},this)}}Headers.prototype.append=function(name,value){name=normalizeName(name);value=normalizeValue(value);var list=this.map[name];if(!list){list=[];this.map[name]=list}list.push(value)};Headers.prototype["delete"]=function(name){delete this.map[normalizeName(name)]};Headers.prototype.get=function(name){var values=this.map[normalizeName(name)];return values?values[0]:null};Headers.prototype.getAll=function(name){return this.map[normalizeName(name)]||[]};Headers.prototype.has=function(name){return this.map.hasOwnProperty(normalizeName(name))};Headers.prototype.set=function(name,value){this.map[normalizeName(name)]=[normalizeValue(value)]};Headers.prototype.forEach=function(callback,thisArg){Object.getOwnPropertyNames(this.map).forEach(function(name){this.map[name].forEach(function(value){callback.call(thisArg,value,name,this)},this)},this)};Headers.prototype.keys=function(){var items=[];this.forEach(function(value,name){items.push(name)});return iteratorFor(items)};Headers.prototype.values=function(){var items=[];this.forEach(function(value){items.push(value)});return iteratorFor(items)};Headers.prototype.entries=function(){var items=[];this.forEach(function(value,name){items.push([name,value])});return iteratorFor(items)};if(support.iterable){Headers.prototype[Symbol.iterator]=Headers.prototype.entries}function consumed(body){if(body.bodyUsed){return Promise.reject(new TypeError("Already read"))}body.bodyUsed=true}function fileReaderReady(reader){return new Promise(function(resolve,reject){reader.onload=function(){resolve(reader.result)};reader.onerror=function(){reject(reader.error)}})}function readBlobAsArrayBuffer(blob){var reader=new FileReader;reader.readAsArrayBuffer(blob);return fileReaderReady(reader)}function readBlobAsText(blob){var reader=new FileReader;reader.readAsText(blob);return fileReaderReady(reader)}function Body(){this.bodyUsed=false;this._initBody=function(body){this._bodyInit=body;if(typeof body==="string"){this._bodyText=body}else if(support.blob&&Blob.prototype.isPrototypeOf(body)){this._bodyBlob=body}else if(support.formData&&FormData.prototype.isPrototypeOf(body)){this._bodyFormData=body}else if(support.searchParams&&URLSearchParams.prototype.isPrototypeOf(body)){this._bodyText=body.toString()}else if(!body){this._bodyText=""}else if(support.arrayBuffer&&ArrayBuffer.prototype.isPrototypeOf(body)){}else{throw new Error("unsupported BodyInit type")}if(!this.headers.get("content-type")){if(typeof body==="string"){this.headers.set("content-type","text/plain;charset=UTF-8")}else if(this._bodyBlob&&this._bodyBlob.type){this.headers.set("content-type",this._bodyBlob.type)}else if(support.searchParams&&URLSearchParams.prototype.isPrototypeOf(body)){this.headers.set("content-type","application/x-www-form-urlencoded;charset=UTF-8")}}};if(support.blob){this.blob=function(){var rejected=consumed(this);if(rejected){return rejected}if(this._bodyBlob){return Promise.resolve(this._bodyBlob)}else if(this._bodyFormData){throw new Error("could not read FormData body as blob")}else{return Promise.resolve(new Blob([this._bodyText]))}};this.arrayBuffer=function(){return this.blob().then(readBlobAsArrayBuffer)};this.text=function(){var rejected=consumed(this);if(rejected){return rejected}if(this._bodyBlob){return readBlobAsText(this._bodyBlob)}else if(this._bodyFormData){throw new Error("could not read FormData body as text")}else{return Promise.resolve(this._bodyText)}}}else{this.text=function(){var rejected=consumed(this);return rejected?rejected:Promise.resolve(this._bodyText)}}if(support.formData){this.formData=function(){return this.text().then(decode)}}this.json=function(){return this.text().then(JSON.parse)};return this}var methods=["DELETE","GET","HEAD","OPTIONS","POST","PUT"];function normalizeMethod(method){var upcased=method.toUpperCase();return methods.indexOf(upcased)>-1?upcased:method}function Request(input,options){options=options||{};var body=options.body;if(Request.prototype.isPrototypeOf(input)){if(input.bodyUsed){throw new TypeError("Already read")}this.url=input.url;this.credentials=input.credentials;if(!options.headers){this.headers=new Headers(input.headers)}this.method=input.method;this.mode=input.mode;if(!body){body=input._bodyInit;input.bodyUsed=true}}else{this.url=input}this.credentials=options.credentials||this.credentials||"omit";if(options.headers||!this.headers){this.headers=new Headers(options.headers)}this.method=normalizeMethod(options.method||this.method||"GET");this.mode=options.mode||this.mode||null;this.referrer=null;if((this.method==="GET"||this.method==="HEAD")&&body){throw new TypeError("Body not allowed for GET or HEAD requests")}this._initBody(body)}Request.prototype.clone=function(){return new Request(this)};function decode(body){var form=new FormData;body.trim().split("&").forEach(function(bytes){if(bytes){var split=bytes.split("=");var name=split.shift().replace(/\+/g," ");var value=split.join("=").replace(/\+/g," ");form.append(decodeURIComponent(name),decodeURIComponent(value))}});return form}function headers(xhr){var head=new Headers;var pairs=(xhr.getAllResponseHeaders()||"").trim().split("\n");pairs.forEach(function(header){var split=header.trim().split(":");var key=split.shift().trim();var value=split.join(":").trim();head.append(key,value)});return head}Body.call(Request.prototype);function Response(bodyInit,options){if(!options){options={}}this.type="default";this.status=options.status;this.ok=this.status>=200&&this.status<300;this.statusText=options.statusText;this.headers=options.headers instanceof Headers?options.headers:new Headers(options.headers);this.url=options.url||"";this._initBody(bodyInit)}Body.call(Response.prototype);Response.prototype.clone=function(){return new Response(this._bodyInit,{status:this.status,statusText:this.statusText,headers:new Headers(this.headers),url:this.url})};Response.error=function(){var response=new Response(null,{status:0,statusText:""});response.type="error";return response};var redirectStatuses=[301,302,303,307,308];Response.redirect=function(url,status){if(redirectStatuses.indexOf(status)===-1){throw new RangeError("Invalid status code")}return new Response(null,{status:status,headers:{location:url}})};self.Headers=Headers;self.Request=Request;self.Response=Response;self.fetch=function(input,init){return new Promise(function(resolve,reject){var request;if(Request.prototype.isPrototypeOf(input)&&!init){request=input}else{request=new Request(input,init)}var xhr=new XMLHttpRequest;function responseURL(){if("responseURL"in xhr){return xhr.responseURL}if(/^X-Request-URL:/m.test(xhr.getAllResponseHeaders())){return xhr.getResponseHeader("X-Request-URL")}return}xhr.onload=function(){var options={status:xhr.status,statusText:xhr.statusText,headers:headers(xhr),url:responseURL()};var body="response"in xhr?xhr.response:xhr.responseText;resolve(new Response(body,options))};xhr.onerror=function(){reject(new TypeError("Network request failed"))};xhr.ontimeout=function(){reject(new TypeError("Network request failed"))};xhr.open(request.method,request.url,true);if(request.credentials==="include"){xhr.withCredentials=true}if("responseType"in xhr&&support.blob){xhr.responseType="blob"}request.headers.forEach(function(value,name){xhr.setRequestHeader(name,value)});xhr.send(typeof request._bodyInit==="undefined"?null:request._bodyInit)})};self.fetch.polyfill=true})(typeof self!=="undefined"?self:this)},{}]},{},[1])(1)}); |
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}(g.dva || (g.dva = {})).fetch = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ | ||
require('whatwg-fetch'); | ||
module.exports = self.fetch.bind(self); | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.default = self.fetch.bind(self); | ||
},{"whatwg-fetch":2}],2:[function(require,module,exports){ | ||
@@ -6,0 +10,0 @@ (function(self) { |
require('whatwg-fetch'); | ||
module.exports = self.fetch.bind(self); | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.default = self.fetch.bind(self); |
@@ -1,2 +0,6 @@ | ||
module.exports = require('./lib'); | ||
module.exports.connect = require('react-redux').connect; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.default = require('./lib'); | ||
exports.connect = require('react-redux').connect; |
@@ -41,6 +41,8 @@ 'use strict'; | ||
var _reduxSaga = require('redux-saga'); | ||
var _middleware = require('redux-saga/lib/internal/middleware'); | ||
var _reduxSaga2 = _interopRequireDefault(_reduxSaga); | ||
var _middleware2 = _interopRequireDefault(_middleware); | ||
var _sagaHelpers = require('redux-saga/lib/internal/sagaHelpers'); | ||
var _handleActions = require('redux-actions/lib/handleActions'); | ||
@@ -70,2 +72,6 @@ | ||
var _window = require('global/window'); | ||
var _window2 = _interopRequireDefault(_window); | ||
var _plugin = require('./plugin'); | ||
@@ -88,2 +94,5 @@ | ||
/** | ||
* Create a dva instance. | ||
*/ | ||
@@ -110,3 +119,3 @@ return function dva() { | ||
// methods | ||
use: plugin.use.bind(plugin), | ||
use: use, | ||
model: model, | ||
@@ -121,9 +130,32 @@ router: router, | ||
function model(m) { | ||
this._models.push(checkModel(m, mobile)); | ||
/** | ||
* Register an object of hooks on the application. | ||
* | ||
* @param hooks | ||
*/ | ||
function use(hooks) { | ||
plugin.use(hooks); | ||
} | ||
/** | ||
* Register a model. | ||
* | ||
* @param model | ||
*/ | ||
function model(model) { | ||
this._models.push(checkModel(model, mobile)); | ||
} | ||
// inject model dynamically | ||
function injectModel(createReducer, onError, m) { | ||
if (m.namespace) { | ||
var hasExisted = this._models.some(function (model) { | ||
return model.namespace === m.namespace; | ||
}); | ||
if (hasExisted) { | ||
return; | ||
} | ||
} | ||
m = checkModel(m, mobile); | ||
this._models.push(m); | ||
var store = this._store; | ||
@@ -144,2 +176,9 @@ | ||
/** | ||
* Config router. Takes a function with arguments { history, dispatch }, | ||
* and expects router config. It use the same api as react-router, | ||
* return jsx elements or JavaScript Object for dynamic routing. | ||
* | ||
* @param router | ||
*/ | ||
function router(router) { | ||
@@ -150,2 +189,8 @@ (0, _invariant2.default)(typeof router === 'function', 'app.router: router should be function'); | ||
/** | ||
* Start the application. Selector is optional. If no selector | ||
* arguments, it will return a function that return JSX elements. | ||
* | ||
* @param container selector | HTMLElement | ||
*/ | ||
function start(container) { | ||
@@ -168,3 +213,3 @@ // support selector | ||
if (typeof err === 'string') err = new Error(err); | ||
onError(err); | ||
onError(err, app._store.dispatch); | ||
} | ||
@@ -212,3 +257,3 @@ }; | ||
var reducerEnhancer = plugin.get('onReducer'); | ||
var sagaMiddleware = (0, _reduxSaga2.default)(); | ||
var sagaMiddleware = (0, _middleware2.default)(); | ||
var middlewares = [sagaMiddleware].concat((0, _toConsumableArray3.default)((0, _flatten2.default)(extraMiddlewares))); | ||
@@ -218,3 +263,3 @@ if (routerMiddleware) { | ||
} | ||
var devtools = window.devToolsExtension || function () { | ||
var devtools = _window2.default.devToolsExtension || function () { | ||
return function (noop) { | ||
@@ -417,2 +462,4 @@ return noop; | ||
var type = 'takeEvery'; | ||
var ms = void 0; | ||
if (Array.isArray(_effect)) { | ||
@@ -423,4 +470,8 @@ effect = _effect[0]; | ||
type = opts.type; | ||
if (type === 'throttle') { | ||
(0, _invariant2.default)(opts.ms, 'app.start: opts.ms should be defined if type is throttle'); | ||
ms = opts.ms; | ||
} | ||
} | ||
(0, _invariant2.default)(['watcher', 'takeEvery', 'takeLatest'].indexOf(type) > -1, 'app.start: effect type should be takeEvery, takeLatest or watcher'); | ||
(0, _invariant2.default)(['watcher', 'takeEvery', 'takeLatest', 'throttle'].indexOf(type) > -1, 'app.start: effect type should be takeEvery, takeLatest, throttle or watcher'); | ||
} | ||
@@ -466,3 +517,3 @@ | ||
var onEffect = plugin.get('onEffect'); | ||
var sagaWithOnEffect = applyOnEffect(onEffect, sagaWithCatch, model); | ||
var sagaWithOnEffect = applyOnEffect(onEffect, sagaWithCatch, model, key); | ||
@@ -479,3 +530,3 @@ switch (type) { | ||
_context3.next = 2; | ||
return (0, _reduxSaga.takeLatest)(key, sagaWithOnEffect); | ||
return (0, _sagaHelpers.takeLatest)(key, sagaWithOnEffect); | ||
@@ -489,4 +540,3 @@ case 2: | ||
}); | ||
// takeEvery | ||
default: | ||
case 'throttle': | ||
return _regenerator2.default.mark(function _callee3() { | ||
@@ -498,3 +548,3 @@ return _regenerator2.default.wrap(function _callee3$(_context4) { | ||
_context4.next = 2; | ||
return (0, _reduxSaga.takeEvery)(key, sagaWithOnEffect); | ||
return (0, _sagaHelpers.throttle)(ms, key, sagaWithOnEffect); | ||
@@ -508,2 +558,19 @@ case 2: | ||
}); | ||
// takeEvery | ||
default: | ||
return _regenerator2.default.mark(function _callee4() { | ||
return _regenerator2.default.wrap(function _callee4$(_context5) { | ||
while (1) { | ||
switch (_context5.prev = _context5.next) { | ||
case 0: | ||
_context5.next = 2; | ||
return (0, _sagaHelpers.takeEvery)(key, sagaWithOnEffect); | ||
case 2: | ||
case 'end': | ||
return _context5.stop(); | ||
} | ||
} | ||
}, _callee4, this); | ||
}); | ||
} | ||
@@ -552,3 +619,3 @@ } | ||
function applyOnEffect(fns, effect, model) { | ||
function applyOnEffect(fns, effect, model, key) { | ||
var _iteratorNormalCompletion4 = true; | ||
@@ -562,3 +629,3 @@ var _didIteratorError4 = false; | ||
effect = fn(effect, sagaEffects, model); | ||
effect = fn(effect, sagaEffects, model, key); | ||
} | ||
@@ -565,0 +632,0 @@ } catch (err) { |
@@ -1,2 +0,6 @@ | ||
module.exports = require('./lib/mobile'); | ||
module.exports.connect = require('react-redux').connect; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.default = require('./lib/mobile'); | ||
exports.connect = require('react-redux').connect; |
{ | ||
"name": "dva", | ||
"version": "1.0.0", | ||
"version": "1.1.0", | ||
"description": "React and redux based, lightweight and elm-style framework.", | ||
@@ -10,2 +10,3 @@ "repository": { | ||
"homepage": "https://github.com/dvajs/dva", | ||
"typings": "./index.d.ts", | ||
"keywords": [ | ||
@@ -23,4 +24,9 @@ "dva", | ||
], | ||
"author": "chencheng <sorrycc@gmail.com>", | ||
"authors": [ | ||
"chencheng <sorrycc@gmail.com> (https://github.com/sorrycc)" | ||
], | ||
"license": "MIT", | ||
"bugs": { | ||
"url": "https://github.com/dvajs/dva/issues" | ||
}, | ||
"scripts": { | ||
@@ -48,2 +54,3 @@ "test": "cross-env NODE_ENV=test nyc mocha --no-timeouts", | ||
"flatten": "^1.0.2", | ||
"global": "^4.3.1", | ||
"invariant": "^2.2.1", | ||
@@ -56,3 +63,3 @@ "is-plain-object": "^2.0.1", | ||
"redux-actions": "^0.12.0", | ||
"redux-saga": "^0.11.1", | ||
"redux-saga": "^0.12.0", | ||
"warning": "^3.0.0", | ||
@@ -106,4 +113,8 @@ "whatwg-fetch": "^1.0.0" | ||
"mobile.js", | ||
"index.js" | ||
"index.js", | ||
"index.d.ts", | ||
"router.d.ts", | ||
"fetch.d.ts", | ||
"mobile.d.ts" | ||
] | ||
} |
275
README.md
@@ -7,105 +7,90 @@ # dva | ||
[![NPM downloads](http://img.shields.io/npm/dm/dva.svg?style=flat)](https://npmjs.org/package/dva) | ||
[![Dependencies](https://david-dm.org/dvajs/dva/status.svg)](https://david-dm.org/dvajs/dva) | ||
React and redux based, lightweight and elm-style framework. | ||
React and redux based, lightweight and elm-style framework. (Inspired by [choo](https://github.com/yoshuawuyts/choo)) | ||
---- | ||
## Documents | ||
## Table of Contents | ||
基础: | ||
- [快速上手](https://github.com/dvajs/dva-docs/blob/master/zh/%E5%BF%AB%E9%80%9F%E4%B8%8A%E6%89%8B.md) | ||
- [基本概念](https://github.com/dvajs/dva-docs/blob/master/zh/concepts/01-%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5.md) | ||
- [Features](#features) | ||
- [Demos](#demos) | ||
- [Getting Started](#getting-started) | ||
- [Creating an App](#creating-an-app) | ||
- [Concepts](#concepts) | ||
- [API](#api) | ||
- [Demos](#demos) | ||
- [dva 简介:Why dva and What's dva](https://github.com/dvajs/dva/issues/1) | ||
- [教程:教你如何一步步完成一个中型应用](https://github.com/dvajs/dva-docs/blob/master/zh/tutorial/01-%E6%A6%82%E8%A6%81.md) | ||
- [升级文档:Upgrade to 1.0.0](https://github.com/dvajs/dva/pull/42#issuecomment-241323617) | ||
- [FAQ](#faq) | ||
- [Read More](#read-more) | ||
- [LICENSE](#license) | ||
扩展阅读: | ||
- [React + Redux 最佳实践](https://github.com/sorrycc/blog/issues/1) (dva 基于此封装) | ||
- [subscription 及其适用场景](https://github.com/dvajs/dva/issues/3#issuecomment-229250708) | ||
- [支付宝前端应用架构的发展和选择: 从 roof 到 redux 再到 dva](https://github.com/sorrycc/blog/issues/6) | ||
- [从 0 开始实现 react 版本的 hackernews (基于 dva)](https://github.com/sorrycc/blog/issues/9) | ||
- [使用 create-react-app 开发 dva 应用](https://github.com/dvajs/dva/issues/58#issuecomment-243435470) | ||
## Features | ||
- **based on redux, redux-saga and react-router** | ||
- **small api:** only 5 methods | ||
- **transparent side effects:** using effects and subscriptions brings clarity to IO | ||
- **mobile and react-native support:** don't need router | ||
- **dynamic model and router:** split large scale app on demand | ||
- **plugin system:** with hooks | ||
- **hmr support:** components and routes is ready | ||
- **based on redux, redux-saga and react-router:** stand on the shoulders of giants | ||
- **small api:** only [5 methods](#api), there's not a lot to learn | ||
- **elm cocepts:** organize model with `reducers`, `effects` and `subscriptions` | ||
- **mobile and react-native support:** cross platform | ||
- **dynamic model and router:** split large app and load on demand | ||
- **plugin system:** make dva extendable, e.g. use [dva-loading](https://github.com/dvajs/dva-loading) to avoid write `showLoading` and `hideLoading` hundreds of times | ||
- **hmr support** with [babel-plugin-dva-hmr](https://github.com/dvajs/babel-plugin-dva-hmr) | ||
- **support typescript:** use [dva-boilerplate-typescript](https://github.com/sorrycc/dva-boilerplate-typescript) for quicking start | ||
## Demos | ||
- [HackerNews](https://dvajs.github.io/dva-hackernews/) ([repo](https://github.com/dvajs/dva-hackernews), [intro](https://github.com/sorrycc/blog/issues/9)) | ||
- [Count](./examples/count) ([jsfiddle](https://jsfiddle.net/puftw0ea/)) | ||
- [Popular Products](./examples/popular-products) | ||
- [Friend List](./examples/friend-list) | ||
- [User Dashboard](./examples/user-dashboard) | ||
- [HackerNews](https://dvajs.github.io/dva-hackernews/) ([repo](https://github.com/dvajs/dva-hackernews), [详解如何一步步实现](https://github.com/sorrycc/blog/issues/9)) | ||
- [Count](examples/count) ([jsfiddle](https://jsfiddle.net/puftw0ea/3/)) | ||
- [Popular Products](examples/popular-products) | ||
- [Friend List](examples/friend-list) | ||
- [User Dashboard](examples/user-dashboard) | ||
## Getting Started | ||
### Install | ||
This is how dva app organized, with only 5 api. View [Count Example](examples/count) for more details. | ||
```bash | ||
$ npm install --save dva | ||
``` | ||
### Example | ||
Let's create an count app that changes when user click the + or - button. | ||
```javascript | ||
import React from 'react'; | ||
import dva, { connect } from 'dva'; | ||
import { Router, Route } from 'dva/router'; | ||
// 1. Initialize | ||
// 1. Create app | ||
const app = dva(); | ||
// 2. Model | ||
app.model({ | ||
namespace: 'count', | ||
state: 0, | ||
reducers: { | ||
add (count) { return count + 1 }, | ||
minus(count) { return count - 1 }, | ||
}, | ||
}); | ||
// 2. Add plugins (optionally) | ||
app.use(plugin); | ||
// 3. View | ||
const App = connect(({ count }) => ({ | ||
count | ||
}))(function(props) { | ||
return ( | ||
<div> | ||
<h2>{ props.count }</h2> | ||
<button key="add" onClick={() => { props.dispatch({type: 'count/add'})}}>+</button> | ||
<button key="minus" onClick={() => { props.dispatch({type: 'count/minus'})}}>-</button> | ||
</div> | ||
); | ||
}); | ||
// 3. Register models | ||
app.model(model); | ||
// 4. Router | ||
app.router(({ history }) => | ||
<Router history={history}> | ||
<Route path="/" component={App} /> | ||
</Router> | ||
); | ||
// 4. Connect components and models | ||
const App = connect(mapStateToProps)(Component); | ||
// 5. Start | ||
// 5. Config router with Components | ||
app.router(routes); | ||
// 6. Start app | ||
app.start('#root'); | ||
``` | ||
You can follow [Getting Started](https://github.com/dvajs/dva-docs/blob/master/v1/en-us/getting-started.md) to make a `Count App` step by step. | ||
## Creating an App | ||
We recommend to use [dva-cli](https://github.com/dvajs/dva-cli) for boilerplating your app. | ||
```bash | ||
// Install dva-cli | ||
$ npm install dva-cli -g | ||
// Create app and start | ||
$ dva new myapp | ||
$ cd myapp | ||
$ npm install | ||
$ npm start | ||
``` | ||
But if you like [create-react-app](https://github.com/facebookincubator/create-react-app), feel free to read [Creating dva app with create-react-app](https://github.com/dvajs/dva/issues/58#issuecomment-243435470). | ||
## Concepts | ||
<p> | ||
<img src="https://zos.alipayobjects.com/rmsportal/PPrerEAKbIoDZYr.png" width="807" /> | ||
</p> | ||
View [Concepts](https://github.com/dvajs/dva-docs/blob/master/v1/en-us/concepts.md) for detail explain on Model, State, Action, dispatch, Reducer, Effect, Subscription, Router and Route Components. | ||
<img src="https://zos.alipayobjects.com/rmsportal/PPrerEAKbIoDZYr.png" width="807" /> | ||
## API | ||
@@ -115,8 +100,8 @@ | ||
Initialize a new `dva` app. opts 里除 `history` 和 `initialState` 外会被传递给 [app.use](#appusehooks) . | ||
Initialize a new `dva` app. Takes an optional object of handlers that is passed to [app.use](#appusehooks). Besides, you can config `history` and `initialState` here. | ||
- `opts.history:` default: `hashHistory` | ||
- `opts.initialState:` default: `{}` | ||
- `opts.history:` the history for router, default: `hashHistory` | ||
- `opts.initialState:` initialState of the app, default: `{}` | ||
`opts.history` 是给路由用的 history,支持 hashHistory 和 browserHistory 。默认 hashHistory,要换成 browserHistory 可以这样: | ||
If you want to use `browserHistory` instead of `hashHistory`: | ||
@@ -130,17 +115,15 @@ ```javascript | ||
`opts.initialState` 是给 store 的初始值,优先级高于 model 里的 state 。 | ||
### `app.use(hooks)` | ||
dva 的插件机制是通过 hooks 实现的,用于添加自定义行为和监听器。 | ||
Register an object of hooks on the application. | ||
目前支持以下 hooks : | ||
Support these `hooks`: | ||
- `onError(err => {}):` effects 和 subscriptions 出错时触发 | ||
- `onAction(Array|Function):` 等同于 redux middleware,支持数组 | ||
- `onStateChange(listener):` 绑定 listner,state 变化时触发 | ||
- `onReducer(reducerEnhancer):` 应用全局的 reducer enhancer,比如 [redux-undo](https://github.com/omnidan/redux-undo) | ||
- `onEffect(Function):` 封装 effect 方法的处理,比如可以实现自动切换 loading 状态 | ||
- `onHmr(render => {}):` 提供 render 方法用于重新渲染 routes 和 components,暂还不支持 model | ||
- `extraReducers(Object):` 提供额外的 reducers,比如 [redux-form](https://github.com/erikras/redux-form) 需要全局 reducer `form` | ||
- `onError(fn):` called when an `effect` or `subscription` emit an error | ||
- `onAction(array|fn):` called when an `action` is dispatched, used for registering redux middleware, support `Array` for convenience | ||
- `onStateChange(fn):` called after a reducer changes the `state` | ||
- `onReducer(fn):` used for apply reducer enhancer | ||
- `onEffect(fn):` used for wrapping effect to add custom behavior, e.g. [dva-loading](https://github.com/dvajs/dva-loading) for automatical loading state | ||
- `onHmr(fn):` used for hot module replacement | ||
- `extraReducers(object):` used for adding extra reducers, e.g. [redux-form](https://github.com/erikras/redux-form) needs extra `form` reducer | ||
@@ -151,11 +134,57 @@ ### `app.model(obj)` | ||
- **namespace:** 通过 namespace 访问其他 model 上的属性,不能为空 | ||
- **state:** 初始值 | ||
- **reducers:** 同步操作,用于更新数据,由 `action` 触发 | ||
- **effects:** 异步操作,处理各种业务逻辑,不直接更新数据,由 `action` 触发,可以 dispatch `action` | ||
- **subscriptions:** 异步只读操作,不直接更新数据,可以 dispatch `action` | ||
- **namespace:** namespace the model | ||
- **state:** initial value | ||
- **reducers:** synchronous operations that modify state. Triggered by `actions`. Signature of `(state, action) => state`, same as Redux. | ||
- **effects:** asynchronous operations that don't modify state directly. Triggered by `actions`, can call `actions`. Signature of `(action, { put, call, select })`, | ||
- **subscriptions:** asynchronous read-only operations that don't modify state directly. Can call `actions`. Signature of `({ dispatch, history })`. | ||
一个典型的 model : | ||
**put(action)** in effects, and **dispatch(action)** in subscriptions | ||
Send a new action to the models. `put` in effects is the same as `dispatch` in subscriptions. | ||
e.g. | ||
```javascript | ||
yield put({ | ||
type: actionType, | ||
payload: attachedData, | ||
error: errorIfHave | ||
}); | ||
``` | ||
or | ||
```javascript | ||
dispatch({ | ||
type: actionType, | ||
payload: attachedData, | ||
error: errorIfHave | ||
}); | ||
``` | ||
When dispatch action inside a `model`, we don't need to add namespace prefix. And if ouside a `model`, we should add namespace separated with a `/`, e.g. `namespace/actionType`. | ||
**call(asyncFunction)** | ||
Call async function. Support promise. | ||
e.g. | ||
```javascript | ||
const result = yield call(api.fetch, { page: 1 }); | ||
``` | ||
**select(function)** | ||
Select data from global state. | ||
e.g. | ||
```javascript | ||
const count = yield select(state => state.count); | ||
``` | ||
A typical model example: | ||
```javascript | ||
app.model({ | ||
@@ -175,3 +204,3 @@ namespace: 'count', | ||
subscriptions: { | ||
// 监听键盘事件,在点击 ctrl + up 时,触发 addDelay action | ||
// Monitor keyboard input | ||
keyboard({ dispatch }) { | ||
@@ -184,18 +213,31 @@ return key('ctrl+up', () => { dispatch({ type: 'addDelay'}); }); | ||
`reducers` 来自 redux,格式为 `(state, action) => state`,详见 [Reducers@redux.js.org](http://redux.js.org/docs/basics/Reducers.html),但不支持 combineReducer 。 | ||
And [another complex model example](https://github.com/dvajs/dva-hackernews/blob/master/src/models/item/index.js) from dva-hackernews. | ||
`effects` 是 side effects,用于存放异步逻辑,底层引入了 [redux-sagas](https://github.com/yelouafi/redux-saga) 做异步流程控制,通过 [generator](http://www.ruanyifeng.com/blog/2015/04/generator.html) 把异步转换成同步写法。格式为 `*(action, effects) => {}`。 | ||
### `app.router(({ history }) => routes)` | ||
`subscriptions` 是订阅,用于订阅一个数据源,然后根据需要 dispatch 相应的 action。数据源可以是当前的时间、服务器的 websocket 连接、keyboard 输入、geolocation 变化、history 路由变化等等。格式为 `({ dispatch, history }) => unsubscribe` 。 | ||
Config router. Takes a function with arguments `{ history }`, and expects `router` config. It use the same api as react-router, return jsx elements or JavaScript Object for dynamic routing. | ||
### `app.router(({ history }) => routes)` | ||
e.g. | ||
创建路由。不做封装,使用和 react-router 相同的配置,可用 jsx 格式,也可用 javascript object 的格式支持动态路由。 | ||
```javascript | ||
import { Router, Route } from 'dva/routes'; | ||
app.router(({ history } => ({ | ||
<Router history={ history }> | ||
<Route path="/" component={App} /> | ||
</Router> | ||
}); | ||
``` | ||
详见:[react-router/docs](https://github.com/reactjs/react-router/tree/master/docs) | ||
More on [react-router/docs](https://github.com/reactjs/react-router/tree/master/docs). | ||
### `app.start(selector?)` | ||
Start the application. 如果没有传入 `selector`,则返回 React Element,可用于 SSR,react-native, 国际化等等。 | ||
Start the application. `selector` is optional. If no `selector` arguments, it will return a function that return JSX elements. | ||
## Installation | ||
```bash | ||
$ npm install dva | ||
``` | ||
## FAQ | ||
@@ -205,10 +247,35 @@ | ||
dva is a [hero](http://ow.blizzard.cn/heroes/dva) from overwatch. She is beautiful and cute, and `dva` is the shortest one that is available on npm. | ||
dva is a [hero](http://ow.blizzard.cn/heroes/dva) from overwatch. She is beautiful and cute, and `dva` is the shortest and available one on npm when creating it. | ||
### Which packages was dva built on? | ||
- views: [react](https://github.com/facebook/react) | ||
- models: [redux](https://github.com/reactjs/redux), [react-redux](https://github.com/reactjs/react-redux), [redux-saga](https://github.com/yelouafi/redux-saga) | ||
- router: [react-router](https://github.com/reactjs/react-router) | ||
- http: [whatwg-fetch](https://github.com/github/fetch) | ||
### Is it production ready? | ||
Yes. | ||
Sure. | ||
### Does it support IE8? | ||
No. | ||
### Does it support react-native? | ||
Yes. Try to get started with [dva-example-react-native](https://github.com/sorrycc/dva-example-react-native). | ||
## Read More | ||
- [dva Knowledgemap](https://github.com/dvajs/dva-knowledgemap) - All knowledge points needed to create a dva app. | ||
- [dva 简介:Why dva and What's dva](https://github.com/dvajs/dva/issues/1) | ||
- [教程:教你如何一步步完成一个中型应用](https://github.com/dvajs/dva-docs/blob/master/v1/zh-cn/tutorial/01-%E6%A6%82%E8%A6%81.md) | ||
- [升级文档:Upgrade to 1.0.0](https://github.com/dvajs/dva/pull/42#issuecomment-241323617) | ||
- [支付宝前端应用架构的发展和选择: 从 roof 到 redux 再到 dva](https://github.com/sorrycc/blog/issues/6) | ||
- [React + Redux 最佳实践](https://github.com/sorrycc/blog/issues/1) | ||
- [从 0 开始实现 react 版本的 hackernews (基于 dva)](https://github.com/sorrycc/blog/issues/9) | ||
## License | ||
[MIT](https://tldrlegal.com/license/mit-license) |
import React from 'react'; | ||
import { Provider } from 'react-redux'; | ||
import { createStore, applyMiddleware, compose, combineReducers } from 'redux'; | ||
import createSagaMiddleware, { takeEvery, takeLatest } from 'redux-saga'; | ||
import createSagaMiddleware from 'redux-saga/lib/internal/middleware'; | ||
import { takeEvery, takeLatest, throttle } from 'redux-saga/lib/internal/sagaHelpers'; | ||
import handleActions from 'redux-actions/lib/handleActions'; | ||
@@ -11,2 +12,3 @@ import * as sagaEffects from 'redux-saga/effects'; | ||
import flatten from 'flatten'; | ||
import window from 'global/window'; | ||
import Plugin from './plugin'; | ||
@@ -25,2 +27,5 @@ | ||
/** | ||
* Create a dva instance. | ||
*/ | ||
return function dva(hooks = {}) { | ||
@@ -44,3 +49,3 @@ // history and initialState does not pass to plugin | ||
// methods | ||
use: plugin.use.bind(plugin), | ||
use, | ||
model, | ||
@@ -55,9 +60,32 @@ router, | ||
function model(m) { | ||
this._models.push(checkModel(m, mobile)); | ||
/** | ||
* Register an object of hooks on the application. | ||
* | ||
* @param hooks | ||
*/ | ||
function use(hooks) { | ||
plugin.use(hooks); | ||
} | ||
/** | ||
* Register a model. | ||
* | ||
* @param model | ||
*/ | ||
function model(model) { | ||
this._models.push(checkModel(model, mobile)); | ||
} | ||
// inject model dynamically | ||
function injectModel(createReducer, onError, m) { | ||
if (m.namespace) { | ||
const hasExisted = this._models.some(model => | ||
model.namespace === m.namespace | ||
); | ||
if (hasExisted) { | ||
return; | ||
} | ||
} | ||
m = checkModel(m, mobile); | ||
this._models.push(m); | ||
const store = this._store; | ||
@@ -78,2 +106,9 @@ | ||
/** | ||
* Config router. Takes a function with arguments { history, dispatch }, | ||
* and expects router config. It use the same api as react-router, | ||
* return jsx elements or JavaScript Object for dynamic routing. | ||
* | ||
* @param router | ||
*/ | ||
function router(router) { | ||
@@ -84,2 +119,8 @@ invariant(typeof router === 'function', 'app.router: router should be function'); | ||
/** | ||
* Start the application. Selector is optional. If no selector | ||
* arguments, it will return a function that return JSX elements. | ||
* | ||
* @param container selector | HTMLElement | ||
*/ | ||
function start(container) { | ||
@@ -102,3 +143,3 @@ // support selector | ||
if (typeof err === 'string') err = new Error(err); | ||
onError(err); | ||
onError(err, app._store.dispatch); | ||
} | ||
@@ -110,3 +151,3 @@ }; | ||
let reducers = { ...initialReducer }; | ||
for (const m of this._models) { | ||
for (let m of this._models) { | ||
reducers[m.namespace] = getReducer(m.reducers, m.state); | ||
@@ -118,3 +159,6 @@ if (m.effects) sagas.push(getSaga(m.effects, m, onErrorWrapper)); | ||
const extraReducers = plugin.get('extraReducers'); | ||
invariant(Object.keys(extraReducers).every(key => !(key in reducers)), 'app.start: extraReducers is conflict with other reducers'); | ||
invariant( | ||
Object.keys(extraReducers).every(key => !(key in reducers)), | ||
'app.start: extraReducers is conflict with other reducers' | ||
); | ||
@@ -157,3 +201,3 @@ // create store | ||
const listeners = plugin.get('onStateChange'); | ||
for (const listener of listeners) { | ||
for (let listener of listeners) { | ||
store.subscribe(listener); | ||
@@ -169,3 +213,3 @@ } | ||
// run subscriptions | ||
for (const model of this._models) { | ||
for (let model of this._models) { | ||
if (model.subscriptions) { | ||
@@ -209,8 +253,26 @@ runSubscriptions(model.subscriptions, model, this, onErrorWrapper); | ||
invariant(namespace, 'app.model: namespace should be defined'); | ||
invariant(mobile || namespace !== 'routing', 'app.model: namespace should not be routing, it\'s used by react-redux-router'); | ||
invariant(!model.subscriptions || isPlainObject(model.subscriptions), 'app.model: subscriptions should be Object'); | ||
invariant(!reducers || isPlainObject(reducers) || Array.isArray(reducers), 'app.model: reducers should be Object or array'); | ||
invariant(!Array.isArray(reducers) || (isPlainObject(reducers[0]) && typeof reducers[1] === 'function'), 'app.model: reducers with array should be app.model({ reducers: [object, function] })') | ||
invariant(!effects || isPlainObject(effects), 'app.model: effects should be Object'); | ||
invariant( | ||
namespace, | ||
'app.model: namespace should be defined' | ||
); | ||
invariant( | ||
mobile || namespace !== 'routing', | ||
'app.model: namespace should not be routing, it\'s used by react-redux-router' | ||
); | ||
invariant( | ||
!model.subscriptions || isPlainObject(model.subscriptions), | ||
'app.model: subscriptions should be Object' | ||
); | ||
invariant( | ||
!reducers || isPlainObject(reducers) || Array.isArray(reducers), | ||
'app.model: reducers should be Object or array' | ||
); | ||
invariant( | ||
!Array.isArray(reducers) || (isPlainObject(reducers[0]) && typeof reducers[1] === 'function'), | ||
'app.model: reducers with array should be app.model({ reducers: [object, function] })' | ||
); | ||
invariant( | ||
!effects || isPlainObject(effects), | ||
'app.model: effects should be Object' | ||
); | ||
@@ -220,3 +282,6 @@ function applyNamespace(type) { | ||
return Object.keys(reducers).reduce((memo, key) => { | ||
warning(key.indexOf(`${namespace}${SEP}`) !== 0, `app.model: ${type.slice(0, -1)} ${key} should not be prefixed with namespace ${namespace}`); | ||
warning( | ||
key.indexOf(`${namespace}${SEP}`) !== 0, | ||
`app.model: ${type.slice(0, -1)} ${key} should not be prefixed with namespace ${namespace}` | ||
); | ||
memo[`${namespace}${SEP}${key}`] = reducers[key]; | ||
@@ -256,3 +321,3 @@ return memo; | ||
return function *() { | ||
for (const key in effects) { | ||
for (let key in effects) { | ||
const watcher = getWatcher(key, effects[key], model, onError); | ||
@@ -267,2 +332,4 @@ yield sagaEffects.fork(watcher); | ||
let type = 'takeEvery'; | ||
let ms; | ||
if (Array.isArray(_effect)) { | ||
@@ -273,4 +340,14 @@ effect = _effect[0]; | ||
type = opts.type; | ||
if (type === 'throttle') { | ||
invariant( | ||
opts.ms, | ||
'app.start: opts.ms should be defined if type is throttle' | ||
); | ||
ms = opts.ms; | ||
} | ||
} | ||
invariant(['watcher', 'takeEvery', 'takeLatest'].indexOf(type) > -1, 'app.start: effect type should be takeEvery, takeLatest or watcher') | ||
invariant( | ||
['watcher', 'takeEvery', 'takeLatest', 'throttle'].indexOf(type) > -1, | ||
'app.start: effect type should be takeEvery, takeLatest, throttle or watcher' | ||
); | ||
} | ||
@@ -287,3 +364,3 @@ | ||
const onEffect = plugin.get('onEffect'); | ||
const sagaWithOnEffect = applyOnEffect(onEffect, sagaWithCatch, model); | ||
const sagaWithOnEffect = applyOnEffect(onEffect, sagaWithCatch, model, key); | ||
@@ -297,2 +374,6 @@ switch (type) { | ||
}; | ||
case 'throttle': | ||
return function*() { | ||
yield throttle(ms, key, sagaWithOnEffect); | ||
}; | ||
// takeEvery | ||
@@ -307,3 +388,3 @@ default: | ||
function runSubscriptions(subs, model, app, onError) { | ||
for (const key in subs) { | ||
for (let key in subs) { | ||
const sub = subs[key]; | ||
@@ -331,3 +412,6 @@ invariant(typeof sub === 'function', 'app.start: subscription should be function'); | ||
invariant(type, 'dispatch: action should be a plain Object with type'); | ||
warning(type.indexOf(`${model.namespace}${SEP}`) !== 0, `effects.put: ${type} should not be prefixed with namespace ${model.namespace}`); | ||
warning( | ||
type.indexOf(`${model.namespace}${SEP}`) !== 0, | ||
`effects.put: ${type} should not be prefixed with namespace ${model.namespace}` | ||
); | ||
return sagaEffects.put({ ...action, type: prefixType(type, model) }); | ||
@@ -342,3 +426,6 @@ } | ||
invariant(type, 'dispatch: action should be a plain Object with type'); | ||
warning(type.indexOf(`${model.namespace}${SEP}`) !== 0, `dispatch: ${type} should not be prefixed with namespace ${model.namespace}`); | ||
warning( | ||
type.indexOf(`${model.namespace}${SEP}`) !== 0, | ||
`dispatch: ${type} should not be prefixed with namespace ${model.namespace}` | ||
); | ||
return dispatch({ ...action, type: prefixType(type, model) }); | ||
@@ -348,5 +435,5 @@ }; | ||
function applyOnEffect(fns, effect, model) { | ||
for (const fn of fns) { | ||
effect = fn(effect, sagaEffects, model); | ||
function applyOnEffect(fns, effect, model, key) { | ||
for (let fn of fns) { | ||
effect = fn(effect, sagaEffects, model, key); | ||
} | ||
@@ -353,0 +440,0 @@ return effect; |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No contributors or author data
MaintenancePackage does not specify a list of contributors or an author in package.json.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No bug tracker
MaintenancePackage does not have a linked bug tracker in package.json.
Found 1 instance in 1 package
1172060
26
19197
275
15
+ Addedglobal@^4.3.1
+ Addeddom-walk@0.1.2(transitive)
+ Addedglobal@4.4.0(transitive)
+ Addedmin-document@2.19.0(transitive)
+ Addedprocess@0.11.10(transitive)
+ Addedredux-saga@0.12.1(transitive)
- Removedredux-saga@0.11.1(transitive)
Updatedredux-saga@^0.12.0