Skip to content

Commit 87d1808

Browse files
authored
Merge pull request #175 from jtassin/master
Debounce of the digests triggered by store modification
2 parents f9494cc + 5324db8 commit 87d1808

File tree

6 files changed

+111
-6
lines changed

6 files changed

+111
-6
lines changed

Diff for: .gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ umd
77
coverage
88
*.tgz
99
examples/**/dist
10+
.idea

Diff for: README.md

+24
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ For Angular 2 see [ng2-redux](https://github.com/wbuchwalter/ng2-redux).
1818
- [API](#api)
1919
- [Dependency Injectable Middleware](#dependency-injectable-middleware)
2020
- [Routers](#routers)
21+
- [Config](#config)
2122
- [Using DevTools](#using-devtools)
2223
- [Additional Resources](#additional-resources)
2324

@@ -199,6 +200,29 @@ $ngReduxProvider.createStoreWith(reducers, [thunk, 'myInjectableMiddleware']);
199200

200201
Middlewares passed as **string** will then be resolved throught angular's injector.
201202

203+
## Config
204+
205+
### Debouncing the digest
206+
You can debounce the digest triggered by store modification (usefull in huge apps with a lot of store modifications) by passing a config parameter to the `ngReduxProvider`.
207+
208+
```javascript
209+
import angular from 'angular';
210+
211+
angular.module('ngapplication').config(($ngReduxProvider) => {
212+
'ngInject';
213+
214+
// eslint-disable-next-line
215+
$ngReduxProvider.config.debounce = {
216+
wait: 100,
217+
maxWait: 500,
218+
};
219+
});
220+
```
221+
222+
This will debounce the digest for 100ms with a maximum delay time of 500ms. Every store modification within this time will be handled by this delayed digest.
223+
224+
[lodash.debounce](https://lodash.com/docs/4.17.4#debounce) is used for the debouncing.
225+
202226
## Routers
203227
See [redux-ui-router](https://github.com/neilff/redux-ui-router) to make ng-redux and UI-Router work together. <br>
204228
See [ng-redux-router](https://github.com/amitport/ng-redux-router) to make ng-redux and angular-route work together.

Diff for: package.json

+1
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
"babel-runtime": "^6.26.0",
6060
"invariant": "^2.2.2",
6161
"lodash.curry": "^4.1.1",
62+
"lodash.debounce": "^4.0.8",
6263
"lodash.isfunction": "^3.0.8",
6364
"lodash.isobject": "^3.0.2",
6465
"lodash.isplainobject": "^4.0.6",

Diff for: src/components/digestMiddleware.js

+12-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,15 @@
1-
export default function digestMiddleware($rootScope) {
1+
import debounce from 'lodash.debounce';
2+
3+
export default function digestMiddleware($rootScope, debounceConfig) {
4+
let debouncedFunction = (expr) => {
5+
$rootScope.$evalAsync(expr);
6+
};
7+
if(debounceConfig && debounceConfig.wait && debounceConfig.wait > 0) {
8+
debouncedFunction = debounce(debouncedFunction, debounceConfig.wait, { maxWait: debounceConfig.maxWait });
9+
}
210
return store => next => action => {
3-
const res = next(action);
4-
$rootScope.$evalAsync(res);
5-
return res;
11+
const res = next(action);
12+
debouncedFunction(res);
13+
return res;
614
};
715
}

Diff for: src/components/ngRedux.js

+9-2
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,13 @@ export default function ngReduxProvider() {
5151
_initialState = initialState || {};
5252
};
5353

54+
this.config = {
55+
debounce: {
56+
wait: undefined,
57+
maxWait: undefined,
58+
},
59+
};
60+
5461
this.$get = ($injector) => {
5562
const resolveMiddleware = middleware => isString(middleware)
5663
? $injector.get(middleware)
@@ -81,13 +88,13 @@ export default function ngReduxProvider() {
8188
}
8289

8390
// digestMiddleware needs to be the last one.
84-
resolvedMiddleware.push(digestMiddleware($injector.get('$rootScope')));
91+
resolvedMiddleware.push(digestMiddleware($injector.get('$rootScope'), this.config.debounce));
8592

8693
// combine middleware into a store enhancer.
8794
const middlewares = applyMiddleware(...resolvedMiddleware);
8895

8996
// compose enhancers with middleware and create store.
90-
const store = createStore(_reducer, _initialState, compose(...resolvedStoreEnhancer, middlewares));
97+
const store = createStore(_reducer, _initialState, compose(middlewares, ...resolvedStoreEnhancer));
9198

9299
const mergedStore = assign({}, store, { connect: Connector(store) });
93100

Diff for: test/components/digestMiddleware.spec.js

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import expect from 'expect';
2+
import sinon from 'sinon';
3+
import digestMiddleware from '../../src/components/digestMiddleware';
4+
5+
6+
describe('digestMiddleware', () => {
7+
8+
it('Should debounce the $evalAsync function if debounce is enabled', (done) => {
9+
const $evalAsync = sinon.spy();
10+
const $rootScope = {
11+
$evalAsync,
12+
};
13+
const firstAction = 1;
14+
const secondAction = 2;
15+
const debounceConfig = {
16+
wait: 10,
17+
};
18+
const next = sinon.spy((action) => (action));
19+
const middleware = digestMiddleware($rootScope, debounceConfig);
20+
middleware()(next)(firstAction);
21+
setTimeout(() => {
22+
middleware()(next)(secondAction);
23+
}, 1);
24+
setTimeout(() => {
25+
expect($evalAsync.calledOnce).toBe(true);
26+
expect(next.calledTwice).toBe(true);
27+
expect(next.firstCall.calledWithExactly(firstAction)).toBe(true);
28+
expect(next.secondCall.calledWithExactly(secondAction)).toBe(true);
29+
expect($evalAsync.firstCall.calledWithExactly(secondAction)).toBe(true);
30+
done();
31+
}, debounceConfig.wait + 10);
32+
33+
});
34+
35+
it('Should not debounce the $evalAsync function if debounce is disabled', () => {
36+
const disabledDebounceConfigs = [
37+
null,
38+
undefined,
39+
{},
40+
{ wait: 0 },
41+
];
42+
disabledDebounceConfigs.forEach(() => {
43+
const $evalAsync = sinon.spy();
44+
const $rootScope = {
45+
$evalAsync,
46+
};
47+
const firstAction = 1;
48+
const secondAction = 2;
49+
const debounceConfig = {};
50+
51+
const next = sinon.spy((action) => (action));
52+
const middleware = digestMiddleware($rootScope, debounceConfig);
53+
middleware()(next)(firstAction);
54+
middleware()(next)(secondAction);
55+
expect($evalAsync.calledTwice).toBe(true);
56+
expect(next.calledTwice).toBe(true);
57+
expect(next.firstCall.calledWithExactly(firstAction)).toBe(true);
58+
expect(next.secondCall.calledWithExactly(secondAction)).toBe(true);
59+
expect($evalAsync.firstCall.calledWithExactly(firstAction)).toBe(true);
60+
expect($evalAsync.secondCall.calledWithExactly(secondAction)).toBe(true);
61+
});
62+
});
63+
64+
});

0 commit comments

Comments
 (0)