Skip to content

Commit 2ae43e2

Browse files
christianalfonigitbook-bot
authored andcommitted
GitBook: [master] 22 pages modified
1 parent 3afe05f commit 2ae43e2

File tree

14 files changed

+357
-491
lines changed

14 files changed

+357
-491
lines changed

README.md

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,9 @@ Building your application with a single state tree is the most straight forward
3333
Separate 3rd party APIs and logic not specific to your application by using **effects**. This will keep your application logic pure and without low level APIs cluttering your code.
3434

3535
{% tabs %}
36-
{% tab title="api.js" %}
37-
```javascript
38-
export const fetchItems = async () {
36+
{% tab title="api.ts" %}
37+
```typescript
38+
export const fetchItems = async (): Promise<Item[]> {
3939
const response = await fetch('/api/items')
4040

4141
return response.json()
@@ -44,9 +44,9 @@ export const fetchItems = async () {
4444
```
4545
{% endtab %}
4646

47-
{% tab title="actions.js" %}
47+
{% tab title="actions.ts" %}
4848
```typescript
49-
export const loadApp = ({ state, effects }) => {
49+
export const loadApp: AsyncAction = ({ state, effects }) => {
5050
state.items = await effects.api.fetchItems()
5151
}
5252
```
@@ -57,8 +57,8 @@ export const loadApp = ({ state, effects }) => {
5757

5858
When you build applications that perform many state changes things can get out of hand. In Overmind you can only perform state changes from **actions** and all changes are tracked by the development tool.
5959

60-
```javascript
61-
export const getItems = async ({ state, effects }) => {
60+
```typescript
61+
export const getItems: AsyncAction = async ({ state, effects }) => {
6262
state.isLoadingItems = true
6363
state.items = await effects.api.fetchItems()
6464
state.isLoadingItems = false
@@ -71,8 +71,8 @@ Even though Overmind can create applications with only plain **state** and **act
7171

7272
{% tabs %}
7373
{% tab title="Operators" %}
74-
```javascript
75-
export const search = pipe(
74+
```typescript
75+
export const search: Operator<string> = pipe(
7676
mutate(({ state }, query) => {
7777
state.query = query
7878
}),
@@ -88,8 +88,16 @@ export const search = pipe(
8888
{% endtab %}
8989

9090
{% tab title="Statechart" %}
91-
```javascript
92-
const loginChart = {
91+
```typescript
92+
const loginChart: Statechart<
93+
typeof config,
94+
{
95+
LOGIN: void
96+
AUTHENTICATING: void
97+
AUTHENTICATED: void
98+
ERROR: void
99+
}
100+
> = {
93101
initial: 'LOGIN',
94102
states: {
95103
LOGIN: {
@@ -121,15 +129,15 @@ const loginChart = {
121129
{% endtab %}
122130

123131
{% tab title="Class state" %}
124-
```javascript
132+
```typescript
125133
class LoginForm() {
126-
private username = ''
127-
private password = ''
128-
private validationError = ''
129-
changeUsername(username) {
134+
private username: string = ''
135+
private password: string = ''
136+
private validationError: string = ''
137+
changeUsername(username: string) {
130138
this.username = username
131139
}
132-
changePassword(password) {
140+
changePassword(password: string) {
133141
if (!password.match([0-9]) {
134142
this.validationError = 'You need some numbers in your password'
135143
}
@@ -140,7 +148,11 @@ class LoginForm() {
140148
}
141149
}
142150

143-
export const state = {
151+
type State = {
152+
loginForm: LoginForm
153+
}
154+
155+
export const state: State = {
144156
loginForm: new LoginForm()
145157
}
146158
```
@@ -151,7 +163,7 @@ export const state = {
151163
152164
Bring in your application configuration of state, effects and actions. Create mocks for any effects. Take a snapshot of mutations performed in an action to ensure all intermediate states are met.
153165
154-
```javascript
166+
```typescript
155167
import { createOvermindMock } from 'overmind'
156168
import { config } from './'
157169

SUMMARY.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,29 +9,29 @@
99

1010
## Core
1111

12-
* [Configuration](core/structuring-the-app.md)
12+
* [Configuration](guides-1/structuring-the-app.md)
1313
* [State](core/defining-state.md)
1414
* [Actions](core/writing-application-logic.md)
1515
* [Effects](core/running-side-effects.md)
1616
* [Operators](core/going-functional.md)
17-
* [Statecharts](core/statecharts.md)
17+
* [Statecharts](features/statecharts.md)
1818
* [Typescript](core/typescript.md)
1919

2020
## Addons
2121

22-
* [React](addons/react.md)
23-
* [Angular](addons/angular.md)
24-
* [Vue](addons/vue.md)
25-
* [GraphQL](addons/graphql.md)
22+
* [React](view-layers/react.md)
23+
* [Angular](view-layers/angular.md)
24+
* [Vue](view-layers/vue.md)
25+
* [GraphQL](features/graphql.md)
2626

2727
## Guides <a id="guides-1"></a>
2828

2929
* [Connecting components](guides-1/connecting-components.md)
3030
* [Managing lists](guides-1/managing-lists.md)
3131
* [State first routing](guides-1/state-first-routing.md)
32-
* [Server Side Rendering](guides-1/server-side-rendering.md)
32+
* [Server Side Rendering](features/server-side-rendering.md)
3333
* [Move to Typescript](guides-1/move-to-typescript.md)
34-
* [Testing](guides-1/testing.md)
34+
* [Testing](features/testing.md)
3535

3636
## API <a id="api-1"></a>
3737

core/defining-state.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ const overmind = createOvermind(config)
240240
{% endtabs %}
241241

242242
{% hint style="info" %}
243-
For scalability you can define **namespaces** for multiple configurations. Read more about that in [Structuring the app](structuring-the-app.md)
243+
For scalability you can define **namespaces** for multiple configurations. Read more about that in [Structuring the app](../guides-1/structuring-the-app.md)
244244
{% endhint %}
245245

246246
## Summary

core/going-functional.md

Lines changed: 60 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,11 @@ What we see here is an action trying to express a search. We only want to search
3232
If we were to do this in a functional style it would look more like this:
3333

3434
{% tabs %}
35-
{% tab title="overmind/actions.js" %}
35+
{% tab title="overmind/actions.ts" %}
3636
```typescript
37-
import { pipe, debounce, mutate, filter } from 'overmind'
37+
import { Operator, pipe, debounce, mutate, filter } from 'overmind'
3838

39-
export const search = pipe(
39+
export const search: Operator<string> = pipe(
4040
mutate(({ state }, value) => {
4141
state.query = value
4242
}),
@@ -65,21 +65,21 @@ You will typically rely on an **operators** file where all your composable piece
6565
Let us look at how the operators in the search example could have been implemented:
6666

6767
{% tabs %}
68-
{% tab title="overmind/operators.js" %}
68+
{% tab title="overmind/operators.ts" %}
6969
```typescript
70-
import {filter, mutate } from 'overmind'
70+
import { Operator, filter, mutate } from 'overmind'
7171

72-
export const setQuery = () =>
72+
export const setQuery: () => Operator<string> = () =>
7373
mutate(function setQuery({ state }, query) {
7474
state.query = query
7575
})
7676

77-
export const lengthGreaterThan = (length) =>
77+
export const lengthGreaterThan: (length: number) => Operator<string> = (length) =>
7878
filter(function lengthGreaterThan(_, value) {
7979
return value.length > length
8080
})
8181

82-
export const getSearchResult = () =>
82+
export const getSearchResult: () => Operator<string> = () =>
8383
mutate(async function getSearchResult({ state, effects }, query) {
8484
state.isSearching = true
8585
state.searchResult = await effects.api.search(query)
@@ -93,6 +93,8 @@ export const getSearchResult = () =>
9393
Note that we give all the actual operator functions the same name as the exported variable that creates it. The reason is that this name is picked up by the devtools and gives you more insight into how your code runs.
9494
{% endhint %}
9595

96+
97+
9698
You might wonder why we define the operators as functions that we call. We do that for the following reasons:
9799

98100
1. It ensures that each composition using the operator has a unique instance of that operator. For most operators this does not matter, but for others like **debounce** it actually matters.
@@ -107,11 +109,11 @@ Now, you might feel that we are just adding complexity here. An additional file
107109
You typically compose the different operators together with **pipe** and **parallel** in the _actions_ file, but any operator can actually be exposed as an action. With the search example:
108110

109111
{% tabs %}
110-
{% tab title="overmind/actions.js" %}
112+
{% tab title="overmind/actions.ts" %}
111113
```typescript
112-
import {pipe, debounce, mutate, filter } from 'overmind'
114+
import { Operator, pipe, debounce, mutate, filter } from 'overmind'
113115

114-
export const search = pipe(
116+
export const search: Operator<string> = pipe(
115117
mutate(({ state }, value) => {
116118
state.query = value
117119
}),
@@ -138,35 +140,39 @@ overmind.actions.search("something")
138140
To produce new values throughout your pipe you can use the **map** operator. It will put whatever value you return from it on the pipe for the next operator to consume.
139141

140142
{% tabs %}
141-
{% tab title="overmind/operators.js" %}
143+
{% tab title="overmind/operators.ts" %}
142144
```typescript
143-
import {map, mutate } from 'overmind'
145+
import { Operator, map, mutate } from 'overmind'
144146

145-
export const toNumber = () =>
147+
export const toNumber: () => Operator<string, number> = () =>
146148
map(function toNumber(_, value) {
147149
return Number(value)
148150
})
149151

150-
export const setValue = () =>
152+
export const setValue: () => Operator<string> = () =>
151153
mutate(function setValue({ state}, value) {
152154
state.value = value
153155
})
154156
```
155157
{% endtab %}
156158

157-
{% tab title="overmind/actions.js" %}
159+
{% tab title="overmind/actions.ts" %}
158160
```typescript
159-
import {pipe } from 'overmind'
161+
import { Operator, pipe } from 'overmind'
160162
import * as o from './operators'
161163

162-
export const onValueChange = pipe(
164+
export const onValueChange: Operator<string, number> = pipe(
163165
o.toNumber(),
164166
o.setValue()
165167
)
166168
```
167169
{% endtab %}
168170
{% endtabs %}
169171

172+
{% hint style="info" %}
173+
Notice here that we are typing both the input and the output. Both for the **map** operator and the **pipe** operator. This is important to manage the composition of operators. Typically operators pass on the same value they receive, meaning that you do not need the second typing parameter for the **Operator** type.
174+
{% endhint %}
175+
170176
## Custom operators
171177

172178
The operators concept of Overmind is based on the [OP-OP SPEC](https://github.com/christianalfoni/op-op-spec), which allows for endless possibilities in functional composition. But since Overmind does not only pass values through these operators, but also the context where you can change state, run effects etc., we want to simplify how you can create your own operators. The added benefit of this is that the operators you create are also tracked in the devtools.
@@ -176,29 +182,29 @@ The operators concept of Overmind is based on the [OP-OP SPEC](https://github.co
176182
Let us create an operator that simply uppercases the string value passed through. This could easily have been done using the **map** operator, but for educational purposes let us see how we can create our very own operator.
177183

178184
{% tabs %}
179-
{% tab title="overmind/operators.js" %}
185+
{% tab title="overmind/operators.ts" %}
180186
```typescript
181-
import {createOperator, mutate } from 'overmind'
187+
import { Operator, createOperator, mutate } from 'overmind'
182188

183-
export const toUpperCase = () => {
189+
export const toUpperCase: () => Operator<string> = () => {
184190
return createOperator('toUpperCase', '', (err, context, value, next) => {
185191
if (err) next(err, value)
186192
else next(null, value.toUpperCase())
187193
})
188194
}
189195

190-
export const setTitle = mutate(({ state }, title) => {
196+
export const setTitle: Operator<string> = mutate(({ state }, title) => {
191197
state.title = title
192198
})
193199
```
194200
{% endtab %}
195201

196-
{% tab title="overmind/actions.js" %}
202+
{% tab title="overmind/actions.ts" %}
197203
```typescript
198-
import { pipe } from 'overmind'
204+
import { Operator, pipe } from 'overmind'
199205
import { toUpperCase, setTitle } from './operators'
200206

201-
export const setUpperCaseTitle = pipe(
207+
export const setUpperCaseTitle: Operator<string> = pipe(
202208
toUpperCase(),
203209
setTitle
204210
)
@@ -215,10 +221,12 @@ In this example we did not use the **context** because we are not going to look
215221
You might want to run some logic related to your operator. Typically this is done by giving a callback. You can provide this callback whatever information you want, even handle its return value. So for example the **map** operator is implemented like this:
216222

217223
```typescript
218-
import { createOperator } from 'overmind'
224+
import { Operator, Context, Config, createOperator } from 'overmind'
219225

220-
export function map(operation) {
221-
return createOperator(
226+
export function map<Input, Output>(
227+
operation: (context: Context, value: Input) => Output
228+
): Operator<Input, Output extends Promise<infer U> ? U : Output> {
229+
return createOperator<Config>(
222230
'map',
223231
operation.name,
224232
(err, context, value, next) => {
@@ -234,10 +242,12 @@ export function map(operation) {
234242
You can also create operators that have the ability to mutate the state, it is just a different factory **createMutationFactory**. This is how the **mutate** operator is implemented:
235243

236244
```typescript
237-
import { createMutationOperator } from 'overmind'
245+
import { Operator, Context, Config, createMutationOperator } from 'overmind'
238246

239-
export function mutate(operation) {
240-
return createMutationOperator(
247+
export function mutate<Input>(
248+
operation: (context: Context, value: Input) => void
249+
): Operator<Input> {
250+
return createMutationOperator<Config>(
241251
'mutate',
242252
operation.name,
243253
(err, context, value, next) => {
@@ -256,10 +266,20 @@ export function mutate(operation) {
256266
You can even manage paths in your operator. This is how the **when** operator is implemented:
257267

258268
```typescript
259-
import { createOperator } from 'overmind'
260-
261-
export function when(operation, paths) {
262-
return createOperator(
269+
import { Operator, Context, Config, createOperator } from 'overmind'
270+
271+
export function when<
272+
Input,
273+
OutputTrue,
274+
OutputFalse
275+
>(
276+
operation: (context: Context, value: Input) => boolean,
277+
paths: {
278+
true: Operator<Input, OutputTrue>
279+
false: Operator<Input, OutputFalse>
280+
}
281+
): Operator<Input, OutputTrue | OutputFalse> {
282+
return createOperator<Config>(
263283
'when',
264284
operation.name,
265285
(err, context, value, next) => {
@@ -284,10 +304,12 @@ export function when(operation, paths) {
284304
Some operators want to prevent further execution. That is also possible to implement, as seen here with the **filter** operator:
285305

286306
```typescript
287-
import { createOperator } from 'overmind'
307+
import { Operator, Context, Config, createOperator } from 'overmind'
288308

289-
export function filter(operation) {
290-
return createOperator(
309+
export function filter<Input>(
310+
operation: (context: Context, value: Input) => boolean
311+
): Operator<Input> {
312+
return createOperator<Config>(
291313
'filter',
292314
operation.name,
293315
(err, context, value, next, final) => {

0 commit comments

Comments
 (0)