Skip to content

Commit

Permalink
feat: Introduced a new function called provideStore() which accepts a…
Browse files Browse the repository at this point in the history
… already created Redux store (angular-redux#142)

* feat: Introduced a new function called provideStore() which accepts a already created Redux store

This function accepts a already created Redux store so the users
can themselves create a store exactly how they want to do it.
The configureStore() function can also be used just like before.

* doc: Fix where constant variable is reassigned (angular-redux#141)

* doc: Fix where constant variable is reassigned

* fix: Update to import syntax in docs

* doc: Fix typos

* doc: Fix typos in changelog

* doc: Fix typos in readme

* doc: Fix typo in readme
  • Loading branch information
drager authored and smithad15 committed Jul 9, 2018
1 parent da98ee3 commit a6c4aaf
Show file tree
Hide file tree
Showing 4 changed files with 242 additions and 36 deletions.
72 changes: 66 additions & 6 deletions packages/store/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,63 @@
# 3.2.0

### Features

* Added a `provideStore()` function which lets you pass in a already created
store. It can be used as this:

Create your store:
```typescript
// store.ts

import {
applyMiddleware,
Store,
combineReducers,
compose,
createStore
} from 'redux';
import thunk from 'redux-thunk';
import reduxLogger from 'redux-logger';

import { myReducer } from './reducers/my-reducer';

const rootReducer = combineReducers({
myReducer,
});

export const store = createStore(
rootReducer,
compose(
applyMiddleware(
thunk,
reduxLogger
)
)
) as Store
```

Create your App and call `provideStore` with your newly created store:
```typescript
// app.ts

import { NgRedux } from 'ng2-redux';
import { store } from './store.ts';

interface IAppState {
// ...
};
@Component({
// ... etc.
})
class App {
constructor(private ngRedux: NgRedux) {
this.ngRedux.provideStore(store);
}

// ...
}
```

# 3.1.0

### Features
Expand All @@ -11,7 +71,7 @@ an observable.
```js
import { is } from 'immutablejs'

export class SomeComponent {
export class SomeComponent {
@select(n=n.some.selector, is) someSelector$: Observable<any>
}

Expand All @@ -29,7 +89,7 @@ export class SomeComponent {
### Features

#### Select Decorator
This release introduces the new decorator interface. You can now use
This release introduces the new decorator interface. You can now use
`@select` to create an observable from a slice of store state.

See 'the select pattern' in [README.md](README.md#the-select-pattern)
Expand Down Expand Up @@ -132,17 +192,17 @@ community: `redux-logger` and `redux-localstorage`.

### Features

* **Type definitions**:
* **Type definitions**:
* Ported to typescript
* Supports typed stores / reducers
* Supports typed stores / reducers
* Uses offical Redux type definitions
* **Type Injectable**:
* Able to inject `NgRedux` into your component by type, and not need `@Inject('ngRedux')`
* `@Inject('ngRedux')` still works

```typescript
import { NgRedux } from 'ng2-redux';
// ...
// ...
export class MyComponent {
constructor(private ngRedux: NgRedux) {}
}
Expand Down Expand Up @@ -181,7 +241,7 @@ export class MyComponent implements OnInit {
person$: Observable<Map<string,any>>;

constructor(private ngRedux: ngRedux) {
// even if the reference of the object has changed,
// even if the reference of the object has changed,
// if the data is the same - it wont be treated as a change
this.person$ = this.ngRedux.select(state=>state.people.get(0),is);
}
Expand Down
106 changes: 98 additions & 8 deletions packages/store/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,62 @@ class App {
}
```

Or if you prefer to create the Redux store yourself you can do that and use the
`provideStore()` function instead.

Create your store:
```typescript
// store.ts

import {
applyMiddleware,
Store,
combineReducers,
compose,
createStore
} from 'redux';
import thunk from 'redux-thunk';
import reduxLogger from 'redux-logger';

import { myReducer } from './reducers/my-reducer';

const rootReducer = combineReducers({
myReducer,
});

export const store = createStore(
rootReducer,
compose(
applyMiddleware(
thunk,
reduxLogger
)
)
) as Store
```

Create your App and call `provideStore` with your newly created store:
```typescript
// app.ts

import { NgRedux } from 'ng2-redux';
import { store } from './store.ts';

interface IAppState {
// ...
};
@Component({
// ... etc.
})
class App {
constructor(private ngRedux: NgRedux) {
this.ngRedux.provideStore(store);
}

// ...
}
```

Now your Angular 2 app has been reduxified!

## Usage
Expand All @@ -85,19 +141,19 @@ preferred approach to accessing store data in Angular 2.

#### The @select decorator

The `@select` decorator can be added to the property of any class or angular
The `@select` decorator can be added to the property of any class or angular
component/injectable. It will turn the property into an observable which observes
the Redux Store value which is selected by the decorator's parameter.

The decorator expects to receive a `string`, an array of `string`s, a `function` or no
parameter at all.
parameter at all.

- If a `string` is passed the `@select` decorator will attempt to observe a store
property whose name matches the `string`.
- If an array of strings is passed, the decorator will attempt to match that path
through the store (similar to `immutableJS`'s `getIn`).
- If a `function` is passed the `@select` decorator will attempt to use that function
as a selector on the RxJs observable.
as a selector on the RxJs observable.
- If nothing is passed then the `@select` decorator will attempt to use the name of the class property to find a matching value in the Redux store. Note that a utility is in place here where any $ characters will be ignored from the class property's name.

```typescript
Expand Down Expand Up @@ -137,7 +193,7 @@ export class CounterValue {
@select(state => state.counter) counterSelectedWithFunction;

// this selects `counter` from the store and multiples it by two
@select(state => state.counter * 2)
@select(state => state.counter * 2)
counterSelectedWithFuntionAndMultipliedByTwo: Observable<any>;
}
```
Expand Down Expand Up @@ -448,7 +504,7 @@ class App {

### `configureStore()`

Initializes your ngRedux store. This should be called once, typically in your
Adds your ngRedux store to NgRedux. This should be called once, typically in your
top-level app component's constructor.

__Arguments:__
Expand All @@ -467,13 +523,47 @@ Async pipe to bind its values into your template.

__Arguments:__

* `key` \(*string*): A key within the state that you want to subscribe to.
* `selector` \(*Function*): A function that accepts the application state, and returns the slice you want subscribe to for changes.
* `key` \(*string*): A key within the state that you want to subscribe to.
* `selector` \(*Function*): A function that accepts the application state, and returns the slice you want subscribe to for changes.

e.g:
```typescript
this.counter$ = this.ngRedux.select(state=>state.counter);
// or
this.counterSubscription = this.ngRedux
.select(state=>state.counter)
.subscribe(count=>this.counter = count);
// or

this.counter$ = this.ngRedux.select('counter');
```

### `provideStore()`

Initializes your ngRedux store. This should be called once, typically in your
top-level app component's constructor. If `configureStore`
has been used this cannot be used.

__Arguments:__

* `store` \(*Store*): Your app's store.

### select(key | function,[comparer]) => Observable

Exposes a slice of state as an observable. Accepts either a property name or a selector function.

If using the async pipe, you do not need to subscribe to it explicitly, but can use the angular
Async pipe to bind its values into your template.

__Arguments:__

* `key` \(*string*): A key within the state that you want to subscribe to.
* `selector` \(*Function*): A function that accepts the application state, and returns the slice you want subscribe to for changes.

e.g:
```typescript
this.counter$ = this.ngRedux.select(state=>state.counter);
// or
// or
this.counterSubscription = this.ngRedux
.select(state=>state.counter)
.subscribe(count=>this.counter = count);
Expand Down
33 changes: 32 additions & 1 deletion packages/store/src/___tests___/components/ng-redux.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,11 +125,12 @@ describe('NgRedux Observable Store', () => {
bar: string;
baz: number;
};

let connector;
let targetObj;
let defaultState;
let rootReducer;
let store;
let ngRedux;
let mockAppRef;

Expand All @@ -153,6 +154,7 @@ describe('NgRedux Observable Store', () => {
}
};

store = createStore(rootReducer);
ngRedux = new NgRedux<IAppState>();
ngRedux.configureStore(rootReducer, defaultState);
});
Expand Down Expand Up @@ -272,4 +274,33 @@ describe('NgRedux Observable Store', () => {
expect(fooData.data).to.equal('update-2');
expect(spy).to.have.been.calledThrice;
});

it('should throw when the store is provided after it has been configured',
() => {
// Configured once in beforeEach, now we try to provide a store when
// we already have configured one.

expect(ngRedux.provideStore.bind(store))
.to.throw(Error);
});

it('should set the store when a store is provided',
() => {

delete ngRedux._store;
delete ngRedux._$store;

expect(ngRedux._store).to.be.undefined;
expect(ngRedux._$store).to.be.undefined;

expect(ngRedux.provideStore.bind(ngRedux, store))
.to.not.throw(Error);

expect(ngRedux._store).to.have.all.keys(
'dispatch',
'subscribe',
'getState',
'replaceReducer'
);
});
});

0 comments on commit a6c4aaf

Please sign in to comment.