Skip to content
This repository was archived by the owner on Feb 16, 2022. It is now read-only.

Commit e2df6fb

Browse files
Piotr Oleśpiotr-oles
authored andcommitted
docs: ✏️ add api documentation for combine and compose fns
In order to make API more consistent, we changed function composeCondition to composeIf BREAKING CHANGE: 🧨 rename conditionDetector function to composeIf to make API more consistent
1 parent d866a27 commit e2df6fb

File tree

6 files changed

+193
-34
lines changed

6 files changed

+193
-34
lines changed

README.md

Lines changed: 30 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ To enable Redux Detector, use `createDetectorEnhancer`:
4141
```js
4242
import { createStore } from "redux";
4343
import { createDetectorEnhancer } from "redux-detector";
44-
import rootReducer from "./reducers";
45-
import rootDetector from "./detectors";
44+
import { rootReducer } from "./store/reducer/rootReducer";
45+
import { rootDetector } from "./store/detector/rootDetector";
4646

4747
const store = createStore(rootReducer, createDetectorEnhancer(rootDetector));
4848
```
@@ -60,12 +60,12 @@ type Detector<TState, TResult> = (
6060
) => TResult;
6161
```
6262

63-
The **`Actions Detector`** is a `Detector` which returns action, list of actions or nothing.
63+
The **`Actions Detector`** is a `Detector` which returns action, list of actions or nothing.
6464
Returned actions are automatically dispatched by the enhancer.
6565

6666
```typescript
67-
import { Detector } from 'redux-detector';
68-
import { Action } from 'redux';
67+
import { Detector } from "redux-detector";
68+
import { Action } from "redux";
6969

7070
type ActionsDetector<TState, TAction extends Action> = Detector<
7171
TState,
@@ -74,53 +74,54 @@ type ActionsDetector<TState, TAction extends Action> = Detector<
7474
```
7575

7676
Another type of the detector is the **`Condition Detector`** which returns boolean values.
77+
7778
```typescript
78-
import { Detector } from 'redux-detector';
79+
import { Detector } from "redux-detector";
7980

8081
type ConditionDetector<TState> = Detector<TState, boolean>;
8182
```
8283

8384
These two types of detectors have different responsibility:
84-
* `Condition Detectors` describes a condition that we want to detect
85-
* `Actions Detectors` describes which action we want to dispatch
85+
86+
- `Condition Detectors` describes a condition that we want to detect
87+
- `Actions Detectors` describes which action we want to dispatch
8688

8789
Thanks to its functional nature and purity, detectors are easy to test. They don't break [Single Source of Truth principle](https://en.wikipedia.org/wiki/Single_source_of_truth)
8890
as the input is only previous and next state.
8991

9092
## Basics 👈
9193

9294
Let's start simply - implement a condition detector that checks if number of login attempts exceeded 3.
95+
9396
```typescript
9497
export const exceededLoginAttemptsLimit = (prevState, nextState) =>
9598
prevState.attempts <= 3 && nextState.attempts > 3;
9699
```
97100

98-
We can make above example more generic - `prevState.attempts <= 3` is the same as `!(prevState.attempts > 3)`.
99-
That means that we check if some condition is **not truthy** for the *previous state* but is **truthy** for the *next state*.
101+
We can make above example more generic - `prevState.attempts <= 3` is the same as `!(prevState.attempts > 3)`.
102+
That means that we check if some condition is **not truthy** for the _previous state_ but is **truthy** for the _next state_.
100103
This kind of transition can be handled by the `changedToTruthy` function.
101104

102105
```typescript
103-
import { changedToTruthy } from 'redux-detector';
106+
import { changedToTruthy } from "redux-detector";
104107

105108
export const exceededLoginAttemptsLimit = changedToTruthy(
106-
(state) => state.attempts > 3
109+
state => state.attempts > 3
107110
);
108111
```
109112

110-
> Redux Detector library provides other useful functions to model condition detectors - please check the
113+
> Redux Detector library provides other useful functions to model condition detectors - please check the
111114
> [API documentation](doc/api.md) to learn more.
112115
113-
The next step is to use an action detector to dispatch an action when the limit became exceeded.
114-
To do so, we will use `conditionDetector` function.
116+
The next step is to use an action detector to dispatch an action when the limit became exceeded.
117+
To do so, we will use `composeIf` function.
118+
115119
```typescript
116-
import {
117-
conditionDetector,
118-
changedToTruthy
119-
} from "redux-detector";
120-
import { blockUser } from '../action/userAction';
121-
122-
const blockUserDetector = conditionDetector(
123-
changedToTruthy((state) => state.attempts > 3),
120+
import { composeIf, changedToTruthy } from "redux-detector";
121+
import { blockUser } from "../action/userAction";
122+
123+
const blockUserDetector = composeIf(
124+
changedToTruthy(state => state.attempts > 3),
124125
() => blockUser()
125126
);
126127
```
@@ -129,18 +130,21 @@ The `createDetectorEnhancer` function accepts only one detector, so we have to c
129130
detectors to the one `rootDetector`.
130131

131132
```typescript
132-
import { composeDetectors } from 'redux-detector';
133-
import { blockUserDetector } from './userDetector';
133+
import { composeDetectors } from "redux-detector";
134+
import { blockUserDetector } from "./userDetector";
134135
// other detectors...
135-
import { companyDetector } from './companyDetector';
136+
import { companyDetector } from "./companyDetector";
136137

137138
export const rootDetector = composeDetectors(
138139
blockUserDetector,
139140
companyDetector
140141
);
141142
```
142143

144+
And that's all - `redux-detector` will dispatch `blockUser()` when login attempts exceeded 3 🎉
145+
143146
## [API reference 📖](doc/api.md)
147+
144148
For more detailed documentation, please check API reference.
145149

146150
## Code splitting ✂️

doc/api.md

Lines changed: 156 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,158 @@
11
## Redux Detector API
22

3-
WIP
3+
The list of all available functions from the `redux-detector` package.
4+
5+
## Redux related API
6+
7+
### `createDetectorEnhancer(detector: ActionsDetector): StoreEnhancer`
8+
9+
Creates new `StoreDetectableEnhancer` that can be passed as a second parameter of the
10+
`createStore` function from `redux`. This enhancer hooks into store's `dispatch` method
11+
and adds `replaceDetector` method.
12+
13+
<details>
14+
<summary>Example:</summary>
15+
16+
```js
17+
import { createStore } from "redux";
18+
import { createDetectorEnhancer } from "redux-detector";
19+
import { rootReducer } from "./store/rootReducer";
20+
import { rootDetector } from "./store/rootDetector";
21+
22+
const store = createStore(rootReducer, createDetectorEnhancer(rootDetector));
23+
```
24+
25+
</details>
26+
27+
### `store.replaceDetector(detector: ActionsDetector): void`
28+
29+
Features like hot-reloading or code splitting requires hooks to update rootDetector.
30+
You can do it via `replaceDetector` method available on an enhanced store.
31+
32+
<details>
33+
<summary>Hot reloading example:</summary>
34+
35+
```js
36+
if (module.hot) {
37+
module.hot.accept("./store/rootReducer", async () => {
38+
const {
39+
rootReducer: nextRootReducer
40+
} = await import("./store/rootReducer");
41+
store.replaceReducer(nextRootReducer);
42+
});
43+
module.hot.accept("./store/rootDetector", async () => {
44+
const {
45+
rootDetector: nextRootDetector
46+
} = await import("./store/rootDetector");
47+
store.replaceDetector(nextRootDetector);
48+
});
49+
}
50+
```
51+
52+
</details>
53+
54+
## Standard library API
55+
56+
These functions are building blocks for creating more complex detectors.
57+
There is no hidden magic in these functions - they are simple and pure.
58+
59+
### `composeDetectors(...detectors: ActionsDetector[]): ActionsDetector`
60+
61+
Composes many actions detectors into one actions detector. If some of detectors
62+
returns an action or a list of actions, they are merged into one list of actions.
63+
64+
<details>
65+
<summary>Example:</summary>
66+
67+
```js
68+
import { composeDetectors } from "redux-detector";
69+
import { userDetector } from "./user/userDetector";
70+
import { companyDetector } from "./company/companyDetector";
71+
72+
export const rootDetector = composeDetectors(userDetector, companyDetector);
73+
```
74+
75+
</details>
76+
77+
### `combineDetectors(map: ActionsDetectorsMap): ActionsDetector`
78+
79+
Combines many actions detectors into one actions detector. The logic behind is the same
80+
as in the `combineReducers` function.
81+
82+
<details>
83+
<summary>Example:</summary>
84+
85+
```js
86+
import { combineDetectors } from "redux-detector";
87+
import { blockUser } from "./userActions";
88+
89+
export const blockUserOnLoginAttemptsExceededDetector = combineDetectors({
90+
login: combineDetectors({
91+
attempts: (prevAttempts, nextAttempts) =>
92+
prevAttempts < 3 && nextAttempts >= 3 ? blockUser() : undefined
93+
})
94+
});
95+
```
96+
97+
</details>
98+
99+
### `composeAnd(...detectors: ConditionDetector[]): ConditionDetector`
100+
101+
Composes many condition detectors into one condition detector using `and` operation on
102+
detectors output.
103+
104+
<details>
105+
<summary>Example:</summary>
106+
107+
```js
108+
import { composeAnd, isTruthy } from "redux-detector";
109+
import { exceededLoginLimit } from "./userDetector";
110+
import { isOnAdminZone } from "../security/securitySelector";
111+
112+
export const exceededLimitOnAdminZone = composeAnd(
113+
isTruthy(isOnAdminZone),
114+
exceededLoginLimit
115+
);
116+
```
117+
118+
</details>
119+
120+
### `composeOr(...detectors: ConditionDetector[]): ConditionDetector`
121+
122+
Composes many condition detectors into one condition detector using `or` operation on
123+
detectors output.
124+
125+
<details>
126+
<summary>Example:</summary>
127+
128+
```js
129+
import { composeOr } from "redux-detector";
130+
import { exceededLoginLimit, exceededResetPasswordLimit } from "./userDetector";
131+
132+
export const exceededLoginOrResetPasswordLimit = composeOr(
133+
exceededLoginLimit,
134+
exceededResetPasswordLimit
135+
);
136+
```
137+
138+
</details>
139+
140+
### `composeIf(condition: ConditionDetector, actions: ActionsDetector): ActionsDetector`
141+
142+
Creates actions detector that runs inner `actions` detector only if `condition` detector
143+
output is truthy.
144+
145+
<details>
146+
<summary>Example:</summary>
147+
148+
```js
149+
import { composeIf } from "redux-detector";
150+
import { exceededLoginLimit } from "./userDetector";
151+
import { blockUser } from "./userActions";
152+
153+
const blockUserOnExceededLimitDetector = composeIf(exceededLoginLimit, () =>
154+
blockUser()
155+
);
156+
```
157+
158+
</details>

src/conditionDetector.ts renamed to src/composeIf.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { ActionsDetector, ConditionDetector } from "./Detector";
22

3-
export function conditionDetector<TState = any, TAction = any>(
3+
export function composeIf<TState = any, TAction = any>(
44
condition: ConditionDetector<TState>,
55
actions: ActionsDetector<TState, TAction>
66
): ActionsDetector<TState, TAction> {

src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@ export { createDetectorEnhancer } from "./createDetectorEnhancer";
99
// standard library
1010
export { composeDetectors } from "./composeDetectors";
1111
export { combineDetectors } from "./combineDetectors";
12-
export { conditionDetector } from "./conditionDetector";
1312
export { mapDetector } from "./mapDetector";
1413
export { mapNextState } from "./mapNextState";
1514
export { mapPrevState } from "./mapPrevState";
15+
export { composeIf } from "./composeIf";
1616
export { composeAnd } from "./composeAnd";
1717
export { composeOr } from "./composeOr";
1818

test/conditionDetector.spec.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
import { conditionDetector } from "../src";
1+
import { composeIf } from "../src";
22

33
describe("conditionDetector", () => {
44
it("should export conditionDetector function", () => {
5-
expect(conditionDetector).toBeInstanceOf(Function);
5+
expect(composeIf).toBeInstanceOf(Function);
66
});
77

88
it("should return conditional detector", () => {
99
const action = { type: "ACTION" };
1010
const detector = jest.fn(() => action);
11-
const conditionalDetector = conditionDetector(
11+
const conditionalDetector = composeIf(
1212
(prevState?: number, nextState?: number) =>
1313
!!(prevState && nextState && prevState < nextState),
1414
detector

test/integration/compose.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import {
22
changedAndTruthy,
33
composeAnd,
4+
composeIf,
45
composeOr,
5-
conditionDetector,
66
isTruthy,
77
mapNextState
88
} from "../../src";
@@ -18,7 +18,7 @@ describe("detectors compose", () => {
1818
const isAPositive = (state: State | undefined) => state && getA(state) > 0;
1919
const action = (payload: any) => ({ type: "DETECTED", payload });
2020

21-
const detector = conditionDetector<State>(
21+
const detector = composeIf<State>(
2222
composeAnd(
2323
isTruthy(isANotNegative),
2424
composeOr(changedAndTruthy(isAZero), isTruthy(isAPositive))

0 commit comments

Comments
 (0)