Skip to content

Commit 92077db

Browse files
christianalfonigitbook-bot
authored andcommitted
GitBook: [master] 4 pages modified
1 parent 26b09ef commit 92077db

File tree

4 files changed

+59
-560
lines changed

4 files changed

+59
-560
lines changed

SUMMARY.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
* [Actions](core/writing-application-logic.md)
1313
* [Effects](core/running-side-effects.md)
1414
* [Operators](core/going-functional.md)
15-
* [Statecharts](features/statecharts.md)
15+
* [Statecharts](core/statecharts.md)
1616
* [Typescript](core/typescript.md)
1717

1818
## Features

core/going-functional.md

Lines changed: 35 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -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.ts" %}
68+
{% tab title="overmind/operators.js" %}
6969
```typescript
70-
import { Operator, filter, mutate } from 'overmind'
70+
import {filter, mutate } from 'overmind'
7171

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

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

82-
export const getSearchResult: () => Operator<string> = () =>
82+
export const getSearchResult = () =>
8383
mutate(async function getSearchResult({ state, effects }, query) {
8484
state.isSearching = true
8585
state.searchResult = await effects.api.search(query)
@@ -93,8 +93,6 @@ export const getSearchResult: () => Operator<string> = () =>
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-
9896
You might wonder why we define the operators as functions that we call. We do that for the following reasons:
9997

10098
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.
@@ -109,11 +107,11 @@ Now, you might feel that we are just adding complexity here. An additional file
109107
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:
110108

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

116-
export const search: Operator<string> = pipe(
114+
export const search = pipe(
117115
mutate(({ state }, value) => {
118116
state.query = value
119117
}),
@@ -140,39 +138,35 @@ overmind.actions.search("something")
140138
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.
141139

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

147-
export const toNumber: () => Operator<string, number> = () =>
145+
export const toNumber = () =>
148146
map(function toNumber(_, value) {
149147
return Number(value)
150148
})
151149

152-
export const setValue: () => Operator<string> = () =>
150+
export const setValue = () =>
153151
mutate(function setValue({ state}, value) {
154152
state.value = value
155153
})
156154
```
157155
{% endtab %}
158156

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

164-
export const onValueChange: Operator<string, number> = pipe(
162+
export const onValueChange = pipe(
165163
o.toNumber(),
166164
o.setValue()
167165
)
168166
```
169167
{% endtab %}
170168
{% endtabs %}
171169

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-
176170
## Custom operators
177171

178172
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.
@@ -182,29 +176,29 @@ The operators concept of Overmind is based on the [OP-OP SPEC](https://github.co
182176
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.
183177

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

189-
export const toUpperCase: () => Operator<string> = () => {
183+
export const toUpperCase = () => {
190184
return createOperator('toUpperCase', '', (err, context, value, next) => {
191185
if (err) next(err, value)
192186
else next(null, value.toUpperCase())
193187
})
194188
}
195189

196-
export const setTitle: Operator<string> = mutate(({ state }, title) => {
190+
export const setTitle = mutate(({ state }, title) => {
197191
state.title = title
198192
})
199193
```
200194
{% endtab %}
201195

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

207-
export const setUpperCaseTitle: Operator<string> = pipe(
201+
export const setUpperCaseTitle = pipe(
208202
toUpperCase(),
209203
setTitle
210204
)
@@ -221,12 +215,10 @@ In this example we did not use the **context** because we are not going to look
221215
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:
222216

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

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>(
220+
export function map(operation) {
221+
return createOperator(
230222
'map',
231223
operation.name,
232224
(err, context, value, next) => {
@@ -242,12 +234,10 @@ export function map<Input, Output>(
242234
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:
243235

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

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

268258
```typescript
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>(
259+
import { createOperator } from 'overmind'
260+
261+
export function when(operation, paths) {
262+
return createOperator(
283263
'when',
284264
operation.name,
285265
(err, context, value, next) => {
@@ -304,12 +284,10 @@ export function when<
304284
Some operators want to prevent further execution. That is also possible to implement, as seen here with the **filter** operator:
305285

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

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

0 commit comments

Comments
 (0)