Skip to content

Commit 3f4017c

Browse files
committed
Refactor ngRedux's api
* Expose store methods on ngRedux * Remove getStore * Remove disableCaching from connector
1 parent 68012eb commit 3f4017c

File tree

4 files changed

+56
-75
lines changed

4 files changed

+56
-75
lines changed

README.md

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
For Angular 2 see [ng2-redux](https://github.com/wbuchwalter/ng2-redux).
55

6-
#####Warning: The API is unstable and subject to breaking changes.
6+
**Warning: The API is unstable and subject to breaking changes.**
77

88
[![build status](https://img.shields.io/travis/wbuchwalter/ng-redux/master.svg?style=flat-square)](https://travis-ci.org/wbuchwalter/ng-redux)
99
[![npm version](https://img.shields.io/npm/v/ng-redux.svg?style=flat-square)](https://www.npmjs.com/package/ng-redux)
@@ -19,7 +19,7 @@ ngRedux lets you easily connect your angular components with Redux.
1919
the API is straightforward:
2020

2121
```JS
22-
$ngRedux.connect(selector, callback, disableCaching = false);
22+
$ngRedux.connect(selector, callback);
2323
```
2424

2525
Where `selector` is a function that takes Redux's entire store state as argument and returns an object that contains the slices of store state that your component is interested in.
@@ -34,9 +34,8 @@ If you haven't, check out [reselect](https://github.com/faassen/reselect), an aw
3434

3535
This returned object will be passed as argument to the callback provided whenever the state changes.
3636
ngRedux checks for shallow equality of the state's selected slice whenever the Store is updated, and will call the callback only if there is a change.
37-
##### Important: It is assumed that you never mutate your states, if you do mutate them, ng-redux will not execute the callback properly.
37+
**Important: It is assumed that you never mutate your states, if you do mutate them, ng-redux will not execute the callback properly.**
3838
See [Redux's doc](http://gaearon.github.io/redux/docs/basics/Reducers.html) to understand why you should not mutate your states.
39-
If you have a good reason to mutate your states, you can still [disable caching](#Disable-caching) altogether.
4039

4140

4241
## Getting Started
@@ -87,14 +86,14 @@ class TodoLoaderController {
8786
}
8887
```
8988

90-
##### Note: The callback provided to ```connect``` will be called once directly after creation to allow initialization of your component states
89+
**Note: The callback provided to `connect` will be called once directly after creation to allow initialization of your component states**
9190

9291

9392

9493
You can also grab multiple slices of the state by passing an array of selectors:
9594

9695
```JS
97-
constructor(reduxConnector) {
96+
constructor($ngRedux) {
9897
this.todos = [];
9998
this.users = [];
10099
$ngRedux.connect(state => ({
@@ -115,9 +114,9 @@ You can close a connection like this:
115114

116115
```JS
117116

118-
constructor(reduxConnector) {
117+
constructor($ngRedux) {
119118
this.todos = [];
120-
this.unsubscribe = reduxConnector.connect(state => ({todos: state.todos}), ({todos}) => this.todos = todos);
119+
this.unsubscribe = $ngRedux.connect(state => ({todos: state.todos}), ({todos}) => this.todos = todos);
121120
}
122121

123122
destroy() {
@@ -127,22 +126,13 @@ destroy() {
127126
```
128127

129128

130-
#### Accessing Redux' Store
131-
You don't need to create another service to get hold of Redux's store (although you can).
132-
You can access the store via ```$ngRedux.getStore()```:
129+
#### Accessing Redux's store methods
130+
All of redux's store methods (i.e. `dispatch`, `subscribe` and `getState`) are exposed by $ngRedux and can be accessed directly. For example:
133131

134132
```JS
135-
redux.bindActionCreators(actionCreator, $ngRedux.getStore().dispatch);
133+
redux.bindActionCreators(actionCreator, $ngRedux.dispatch);
136134
```
137-
138-
#### Disabling caching
139-
Each time Redux's Store update, ng-redux will check if the slices specified via 'selectors' have changed, and if so will execute the provided callback.
140-
You can disable this behaviour, and force the callback to be executed even if the slices didn't change by setting ```disableCaching``` to true:
141-
142-
```JS
143-
reduxConnector.connect(state => ({todos: state.todos}), ({todos}) => this.todos = todos, true);
144-
```
145-
135+
**Note:** If you choose to use `subscribe` directly, be sure to [unsubscribe](#unsubscribing) when your current scope is $destroyed.
146136

147137
### Example:
148138
An example can be found here (in TypeScript): [tsRedux](https://github.com/wbuchwalter/tsRedux/blob/master/src/components/regionLister.ts).

src/components/connector.js

Lines changed: 17 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,30 +3,23 @@ import shallowEqual from '../utils/shallowEqual';
33
import invariant from 'invariant';
44

55
export default function Connector(store) {
6-
return {
7-
connect: (selector, callback, disableCaching = false) => {
8-
invariant(
9-
isFunction(callback),
10-
'The callback parameter passed to connect must be a Function. Instead received %s.',
11-
typeof callback
12-
);
6+
return (selector, callback) => {
7+
invariant(
8+
isFunction(callback),
9+
'The callback parameter passed to connect must be a Function. Instead received %s.',
10+
typeof callback
11+
);
1312

14-
//Initial update
15-
let params = selector(store.getState());
16-
callback(params);
13+
//Initial update
14+
let params = selector(store.getState());
15+
callback(params);
1716

18-
let unsubscribe = store.subscribe(() => {
19-
let nextParams = selector(store.getState());
20-
if (disableCaching || !shallowEqual(params, nextParams)) {
21-
callback(nextParams);
22-
params = nextParams;
23-
}
24-
});
25-
26-
return unsubscribe;
27-
},
28-
getStore() {
29-
return store;
30-
}
17+
return store.subscribe(() => {
18+
let nextParams = selector(store.getState());
19+
if (!shallowEqual(params, nextParams)) {
20+
callback(nextParams);
21+
params = nextParams;
22+
}
23+
});
3124
}
32-
}
25+
}

src/components/ngRedux.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ export default function ngReduxProvider() {
2727
};
2828

2929
this.$get = ($injector) => {
30-
let resolvedMiddleware = [];
30+
let store, resolvedMiddleware = [];
31+
3132
for(let middleware of _middlewares) {
3233
if(typeof middleware === 'string') {
3334
resolvedMiddleware.push($injector.get(middleware));
@@ -36,6 +37,11 @@ export default function ngReduxProvider() {
3637
}
3738
}
3839

39-
return Connector(applyMiddleware(...resolvedMiddleware)(_storeEnhancer)(_reducer));
40+
store = applyMiddleware(...resolvedMiddleware)(_storeEnhancer)(_reducer);
41+
42+
return {
43+
...store,
44+
connector: Connector(store)
45+
};
4046
}
4147
}

test/components/connector.spec.js

Lines changed: 20 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,31 +4,34 @@ import Connector from '../../src/components/connector';
44

55
describe('Connector', () => {
66
let store;
7-
let connector;
7+
let connect;
8+
89
beforeEach(() => {
9-
store = createStore((state, action) => {
10-
return {foo: 'bar', baz: action.payload, anotherState: 12};
11-
});
12-
connector = Connector(store);
10+
store = createStore((state, action) => ({
11+
foo: 'bar',
12+
baz: action.payload,
13+
anotherState: 12
14+
}));
15+
connect = Connector(store);
1316
});
1417

1518
it('Should throw when not passed a function as callback', () => {
16-
expect(connector.connect.bind(connector, () => {}, undefined)).toThrow();
17-
expect(connector.connect.bind(connector, () => {}, {})).toThrow();
18-
expect(connector.connect.bind(connector, () => {}, 15)).toThrow();
19+
expect(connect.bind(connect, () => {}, undefined)).toThrow();
20+
expect(connect.bind(connect, () => {}, {})).toThrow();
21+
expect(connect.bind(connect, () => {}, 15)).toThrow();
1922
});
2023

2124
it('Callback should be called once directly after creation to allow initialization', () => {
2225
let counter = 0;
2326
let callback = () => counter++;
24-
connector.connect(state => state, callback);
27+
connect(state => state, callback);
2528
expect(counter).toBe(1);
2629
});
2730

2831
it('Should call the callback passed to connect when the store updates', () => {
2932
let counter = 0;
3033
let callback = () => counter++;
31-
connector.connect(state => state, callback);
34+
connect(state => state, callback);
3235
store.dispatch({type: 'ACTION', payload: 0});
3336
store.dispatch({type: 'ACTION', payload: 1});
3437
expect(counter).toBe(3);
@@ -37,33 +40,23 @@ describe('Connector', () => {
3740
it('Should prevent unnecessary updates when state does not change (shallowly)', () => {
3841
let counter = 0;
3942
let callback = () => counter++;
40-
connector.connect(state => ({baz: state.baz}), callback);
43+
connect(state => ({baz: state.baz}), callback);
4144
store.dispatch({type: 'ACTION', payload: 0});
4245
store.dispatch({type: 'ACTION', payload: 0});
4346
store.dispatch({type: 'ACTION', payload: 1});
4447
expect(counter).toBe(3);
4548
});
4649

47-
it('Should disable caching when disableCaching is set to true', () => {
48-
let counter = 0;
49-
let callback = () => counter++;
50-
connector.connect(state => ({baz: state.baz}), callback, true);
51-
store.dispatch({type: 'ACTION', payload: 0});
52-
store.dispatch({type: 'ACTION', payload: 0});
53-
store.dispatch({type: 'ACTION', payload: 1});
54-
expect(counter).toBe(4);
55-
});
56-
5750
it('Should pass the selected state as argument to the callback', () => {
58-
let receivedState;
59-
connector.connect(state => ({
51+
connect(state => ({
6052
myFoo: state.foo
61-
}), newState => receivedState = newState);
62-
expect(receivedState).toEqual({myFoo: 'bar'});
53+
}), newState => {
54+
expect(newState).toEqual({myFoo: 'bar'});
55+
});
6356
});
6457

6558
it('Should allow multiple store slices to be selected', () => {
66-
connector.connect(state => ({
59+
connect(state => ({
6760
foo: state.foo,
6861
anotherState: state.anotherState
6962
}), ({foo, anotherState}) => {
@@ -75,11 +68,10 @@ describe('Connector', () => {
7568
it('Should return an unsubscribing function', () => {
7669
let counter = 0;
7770
let callback = () => counter++;
78-
let unsubscribe = connector.connect(state => state, callback);
71+
let unsubscribe = connect(state => state, callback);
7972
store.dispatch({type: 'ACTION', payload: 0});
8073
unsubscribe();
8174
store.dispatch({type: 'ACTION', payload: 2});
8275
expect(counter).toBe(2);
8376
});
84-
8577
});

0 commit comments

Comments
 (0)