Skip to content
This repository has been archived by the owner on Jan 15, 2023. It is now read-only.

Commit

Permalink
refactored selectors module; added selectors docs section
Browse files Browse the repository at this point in the history
  • Loading branch information
markostanimirovic committed Sep 2, 2020
1 parent 6ad87ab commit 19a8abf
Show file tree
Hide file tree
Showing 7 changed files with 109 additions and 88 deletions.
43 changes: 34 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

<img alt="Juliette in Action" src="https://i.ibb.co/XJYB8HN/juliette-in-action.gif" width="600" />

## Table of Contents
## Contents

- [Overview](#overview)
- [Reduced Boilerplate Without Conditional Branching](#reduced-boilerplate-without-conditional-branching)
Expand All @@ -19,6 +19,7 @@
- [Guide](#guide)
- [Handlers](#handlers)
- [Store](#store)
- [Selectors](#selectors)
- [Effects](#effects)
- [Angular Plugin](#angular-plugin)
- [React Plugin](#react-plugin)
Expand Down Expand Up @@ -317,6 +318,31 @@ In case you need to initialize a feature state on the fly, there is `addFeatureS
store.addFeatureState(fromTodos.featureKey, fromTodos.initialState);
```

### Selectors

Juliette provides `composeSelectors` function for selector composition. It accepts an array of selector functions as the first argument
and composer function as the second argument.

```typescript
const selectTodosState: Selector<AppState, fromTodos.State> = state =>
state[fromTodos.featureKey];
const selectAllTodos = composeSelectors([selectTodosState], state => state.todos);

const selectInProgressTodos = composeSelectors(
[selectAllTodos],
todos => todos.filter(todo => todo.status === 'IN_PROGRESS'),
);
const selectDoneTodos = composeSelectors(
[selectAllTodos],
todos => todos.filter(todo => todo.status === 'DONE'),
);

const selectInProgressAndDoneTodos = composeSelectors(
[selectInProgressTodos, selectDoneTodos],
(inProgressTodos, doneTodos) => [...inProgressTodos, ...doneTodos],
);
```

### Effects

If you need to perform a side effect when some handler is dispatched, the effect component is the right place to do that. This approach to managing
Expand Down Expand Up @@ -443,14 +469,14 @@ export class TodosComponent {
}
```

To initialize the feature state in lazy-loaded module, use `forChild` method.
To initialize the feature state in lazy-loaded module, use `forFeature` method.

```typescript
@NgModule({
...
imports: [
...
StoreModule.forChild(fromTodos.featureKey, fromTodos.initialState),
StoreModule.forFeature(fromTodos.featureKey, fromTodos.initialState),
],
})
export class TodosModule {}
Expand All @@ -463,15 +489,14 @@ To register the effects, JulietteNg provides `EffectsModule`.
...
imports: [
...
StoreModule.forRoot(initialAppState, !environment.production),
EffectsModule.register([TodosEffects]),
],
})
export class AppModule {}
```

`register` method from `EffectsModule` accepts an array of classes with effects. By creating effects within the class, you can use all the benefits
of dependency injection.
`register` method from `EffectsModule` accepts an array of classes with effects and can be used in both, root and feature modules.
By creating effects within the class, you can use all the benefits of dependency injection.

```typescript
@Injectable()
Expand All @@ -495,7 +520,7 @@ export class TodosEffects {

JulietteReact library contains custom hooks for easier state accessibility within the React components. To use them, provide the store via `StoreContext`.

```typescript
```typescript jsx
ReactDOM.render(
<StoreContext.Provider value={store}>
<App />
Expand All @@ -506,7 +531,7 @@ ReactDOM.render(

This plugin provides `useSelect` hook that accepts a selector function or feature key and `useDispatch` hook that returns the dispatch function.

```typescript
```typescript jsx
function Todos() {
const todosState = useSelect<AppState, fromTodos.State>(fromTodos.featureKey);
const dispatch = useDispatch();
Expand Down Expand Up @@ -538,7 +563,7 @@ as a state management solution.

- Selector composition ✔️
- State immutability runtime checks ✔️
- Support for lazy loading feature modules ✔️
- Support for lazy-loaded modules ✔️

## Support

Expand Down
4 changes: 2 additions & 2 deletions projects/juliette-ng/package.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"name": "juliette-ng",
"version": "0.12.0",
"version": "0.14.0",
"peerDependencies": {
"juliette": "0.12.0",
"juliette": "0.14.0",
"@angular/core": "^10.0.5",
"rxjs": "^6.5.5"
},
Expand Down
4 changes: 2 additions & 2 deletions projects/juliette-react/package.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"name": "juliette-react",
"version": "0.12.0",
"version": "0.14.0",
"main": "public-api.js",
"types": "public-api.d.ts",
"peerDependencies": {
"juliette": "0.12.0",
"juliette": "0.14.0",
"react": "^16.13.1"
}
}
2 changes: 1 addition & 1 deletion projects/juliette/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "juliette",
"version": "0.12.0",
"version": "0.14.0",
"main": "public-api.js",
"types": "public-api.d.ts",
"peerDependencies": {
Expand Down
123 changes: 60 additions & 63 deletions projects/juliette/src/lib/selectors.ts
Original file line number Diff line number Diff line change
@@ -1,95 +1,92 @@
import { Selector } from './models';

export function createFeatureSelector<T, R extends T[keyof T]>(
featureKey: keyof T,
): Selector<T, R> {
return state => state[featureKey] as R;
}

export function createSelector<T, R, S>(
s: Selector<T, S>,
export function composeSelectors<T, R, S>(
selectors: [Selector<T, S>],
composer: Selector<S, R>,
): Selector<T, R>;

export function createSelector<T, R, S1, S2>(
s1: Selector<T, S1>,
s2: Selector<T, S2>,
export function composeSelectors<T, R, S1, S2>(
selectors: [Selector<T, S1>, Selector<T, S2>],
composer: (s1: S1, s2: S2) => R,
): Selector<T, R>;

export function createSelector<T, R, S1, S2, S3>(
s1: Selector<T, S1>,
s2: Selector<T, S2>,
s3: Selector<T, S3>,
export function composeSelectors<T, R, S1, S2, S3>(
selectors: [Selector<T, S1>, Selector<T, S2>, Selector<T, S3>],
composer: (s1: S1, s2: S2, s3: S3) => R,
): Selector<T, R>;

export function createSelector<T, R, S1, S2, S3, S4>(
s1: Selector<T, S1>,
s2: Selector<T, S2>,
s3: Selector<T, S3>,
s4: Selector<T, S4>,
export function composeSelectors<T, R, S1, S2, S3, S4>(
selectors: [Selector<T, S1>, Selector<T, S2>, Selector<T, S3>, Selector<T, S4>],
composer: (s1: S1, s2: S2, s3: S3, s4: S4) => R,
): Selector<T, R>;

export function createSelector<T, R, S1, S2, S3, S4, S5>(
s1: Selector<T, S1>,
s2: Selector<T, S2>,
s3: Selector<T, S3>,
s4: Selector<T, S4>,
s5: Selector<T, S5>,
export function composeSelectors<T, R, S1, S2, S3, S4, S5>(
selectors: [Selector<T, S1>, Selector<T, S2>, Selector<T, S3>, Selector<T, S4>, Selector<T, S5>],
composer: (s1: S1, s2: S2, s3: S3, s4: S4, s5: S5) => R,
): Selector<T, R>;

export function createSelector<T, R, S1, S2, S3, S4, S5, S6>(
s1: Selector<T, S1>,
s2: Selector<T, S2>,
s3: Selector<T, S3>,
s4: Selector<T, S4>,
s5: Selector<T, S5>,
s6: Selector<T, S6>,
export function composeSelectors<T, R, S1, S2, S3, S4, S5, S6>(
selectors: [
Selector<T, S1>,
Selector<T, S2>,
Selector<T, S3>,
Selector<T, S4>,
Selector<T, S5>,
Selector<T, S6>,
],
composer: (s1: S1, s2: S2, s3: S3, s4: S4, s5: S5, s6: S6) => R,
): Selector<T, R>;

export function createSelector<T, R, S1, S2, S3, S4, S5, S6, S7>(
s1: Selector<T, S1>,
s2: Selector<T, S2>,
s3: Selector<T, S3>,
s4: Selector<T, S4>,
s5: Selector<T, S5>,
s6: Selector<T, S6>,
s7: Selector<T, S7>,
export function composeSelectors<T, R, S1, S2, S3, S4, S5, S6, S7>(
selectors: [
Selector<T, S1>,
Selector<T, S2>,
Selector<T, S3>,
Selector<T, S4>,
Selector<T, S5>,
Selector<T, S6>,
Selector<T, S7>,
],
composer: (s1: S1, s2: S2, s3: S3, s4: S4, s5: S5, s6: S6, s7: S7) => R,
): Selector<T, R>;

export function createSelector<T, R, S1, S2, S3, S4, S5, S6, S7, S8>(
s1: Selector<T, S1>,
s2: Selector<T, S2>,
s3: Selector<T, S3>,
s4: Selector<T, S4>,
s5: Selector<T, S5>,
s6: Selector<T, S6>,
s7: Selector<T, S7>,
s8: Selector<T, S8>,
export function composeSelectors<T, R, S1, S2, S3, S4, S5, S6, S7, S8>(
selectors: [
Selector<T, S1>,
Selector<T, S2>,
Selector<T, S3>,
Selector<T, S4>,
Selector<T, S5>,
Selector<T, S6>,
Selector<T, S7>,
Selector<T, S8>,
],
composer: (s1: S1, s2: S2, s3: S3, s4: S4, s5: S5, s6: S6, s7: S7, s8: S8) => R,
): Selector<T, R>;

export function createSelector<T, R, S1, S2, S3, S4, S5, S6, S7, S8, S9>(
s1: Selector<T, S1>,
s2: Selector<T, S2>,
s3: Selector<T, S3>,
s4: Selector<T, S4>,
s5: Selector<T, S5>,
s6: Selector<T, S6>,
s7: Selector<T, S7>,
s8: Selector<T, S8>,
s9: Selector<T, S9>,
export function composeSelectors<T, R, S1, S2, S3, S4, S5, S6, S7, S8, S9>(
selectors: [
Selector<T, S1>,
Selector<T, S2>,
Selector<T, S3>,
Selector<T, S4>,
Selector<T, S5>,
Selector<T, S6>,
Selector<T, S7>,
Selector<T, S8>,
Selector<T, S9>,
],
composer: (s1: S1, s2: S2, s3: S3, s4: S4, s5: S5, s6: S6, s7: S7, s8: S8, s9: S9) => R,
): Selector<T, R>;

export function createSelector<T, R>(...functions: ((...args: any) => any)[]): Selector<T, R> {
const composer = functions.pop() as (...states: any[]) => R;
const selectors = functions as Selector<T, any>[];
export function composeSelectors<T, R>(
selectors: Selector<T, any>[],
composer: (...states: any[]) => R,
): Selector<T, R>;

export function composeSelectors<T, R>(
selectors: Selector<T, any>[],
composer: (...states: any[]) => R,
): Selector<T, R> {
return state => composer(...selectors.map(selector => selector(state)));
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { createFeatureSelector, createSelector } from 'juliette';
import { composeSelectors } from 'juliette';
import { Feature1AppState, fromFeature1 } from './index';
import { selectUsers } from '../../store/selectors';
import { Selector } from '../../../../../juliette/src/lib/models';

export const selectFeature1State = createFeatureSelector<Feature1AppState, fromFeature1.State>(
fromFeature1.featureKey,
);
export const selectFoo = createSelector(selectFeature1State, state => state.foo);
export const selectFooWithUsers = createSelector(selectFoo, selectUsers, (foo, users) => ({
export const selectFeature1State: Selector<Feature1AppState, fromFeature1.State> = state =>
state[fromFeature1.featureKey];
export const selectFoo = composeSelectors([selectFeature1State], state => state.foo);
export const selectFooWithUsers = composeSelectors([selectFoo, selectUsers], (foo, users) => ({
foo,
users,
}));
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { createFeatureSelector, createSelector } from 'juliette';
import { Selector, composeSelectors } from 'juliette';
import { fromUsers } from '../handlers';
import { AppState } from '../index';

export const selectUsersState = createFeatureSelector<AppState, fromUsers.State>(
fromUsers.featureKey,
);
export const selectUsers = createSelector(selectUsersState, state => state.users);
export const selectUsersState: Selector<AppState, fromUsers.State> = state =>
state[fromUsers.featureKey];
export const selectUsers = composeSelectors([selectUsersState], state => state.users);

0 comments on commit 19a8abf

Please sign in to comment.