Skip to content

Commit b60149f

Browse files
christianalfonigitbook-bot
authored andcommitted
GitBook: [master] 13 pages modified
1 parent 63a33bd commit b60149f

File tree

11 files changed

+81
-173
lines changed

11 files changed

+81
-173
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ description: frictionless state management
66

77
> Web application development is about **defining**, **changing** and **consuming state** to produce a user experience. Overmind aims for a developer experience where that is all you focus on, reducing the orchestration of state management to a minimum. Making you a **happier** and more **productive** developer!
88
9+
{% embed url="https://overmindjs.changefeed.app/general/v22.0.0" %}
10+
911
## APPLICATION INSIGHT
1012

1113
Develop the application state, effects and actions without leaving [VS Code](https://code.visualstudio.com/), or use the standalone development tool. Everything that happens in your app is tracked and you can seamlessly code and run logic to verify that everything works as expected without necessarily having to implement UI.

SUMMARY.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
* [Effects](core/running-side-effects.md)
1717
* [Operators](core/going-functional.md)
1818
* [Statecharts](core/statecharts.md)
19-
* [Server Side Rendering](core/server-side-rendering.md)
19+
* [Server Side Rendering](guides-1/server-side-rendering.md)
2020
* [Typescript](core/typescript.md)
2121

2222
## views
@@ -36,7 +36,7 @@
3636
* [Managing lists](guides-1/managing-lists.md)
3737
* [State first routing](guides-1/state-first-routing.md)
3838
* [Move to Typescript](guides-1/move-to-typescript.md)
39-
* [Testing](guides-1/testing.md)
39+
* [Testing](features/testing.md)
4040

4141
## API <a id="api-1"></a>
4242

core/defining-state.md

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -113,10 +113,6 @@ export const state = {
113113
{% endtab %}
114114
{% endtabs %}
115115

116-
{% hint style="warning" %}
117-
It is import that you do **NOT** use arrow functions on your methods. The reason is that this binds the context of the method to the instance itself, meaning that Overmind is unable to proxy access and allow you to do tracked mutations
118-
{% endhint %}
119-
120116
You can now use this instance as normal and of course create new ones.
121117

122118
{% hint style="info" %}
@@ -388,11 +384,11 @@ export const login = async ({ state, effects }) => {
388384
return state.mode.authenticating(async () => {
389385
try {
390386
const user = await effects.api.getUser()
391-
return state.mode.authenticated(() => {
387+
state.mode.authenticated(() => {
392388
state.user = user
393389
})
394390
} catch (error) {
395-
return state.mode.unauthenticated(() => {
391+
state.mode.unauthenticated(() => {
396392
state.error = error
397393
})
398394
}
@@ -403,9 +399,9 @@ export const logout = async ({ state, effects }) => {
403399
return state.mode.unauthenticating(async () => {
404400
try {
405401
await effects.api.logout()
406-
return state.mode.unauthenticated()
402+
state.mode.unauthenticated()
407403
} catch (error) {
408-
return state.mode.authenticated(() => {
404+
state.mode.authenticated(() => {
409405
state.error = error
410406
})
411407
}
@@ -415,11 +411,8 @@ export const logout = async ({ state, effects }) => {
415411
{% endtab %}
416412
{% endtabs %}
417413

418-
{% hint style="warning" %}
419-
There are two important rules for predictable transitions:
420-
421-
1. The transition should always be **returned**
422-
2. Only **synchronous** transitions can mutate the state
414+
{% hint style="info" %}
415+
If your transition runs asynchronously you should return the transition to ensure that the action execution is tracked
423416
{% endhint %}
424417

425418
What is important to realize here is that our logic is separated into **allowable** transitions. That means when we are waiting for the user on **line 4** and some other logic has changed the state to **unauthenticated** in the meantime, the user will not be set, as the **authenticated** transition is now not possible. This is what state machines do. They group logic into states that are allowed to run, preventing invalid logic to run.

core/running-side-effects.md

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,6 @@ It can be a good idea to not allow your side effects to initialize when they are
9191
```typescript
9292
import * as firebase from 'firebase'
9393

94-
// We use IIFE to hide the private "app" variable
9594
export const api = (() => {
9695
let app
9796

@@ -133,35 +132,6 @@ export const onInitialize = async ({ effects }) => {
133132
Typically you explicitly communicate with effects from actions, by calling methods. But sometimes you need effects to know about the state of the application, or maybe some internal state in the effect should be exposed on your application state. Again we can take advantage of an **initialize** method on the effect:
134133

135134
{% tabs %}
136-
{% tab title="overmind/effects.js" %}
137-
```javascript
138-
// We use an IIFE to isolate some variables
139-
export const socket = (() => {
140-
_options
141-
_ws
142-
return {
143-
initialize(options) {
144-
_options = options
145-
_ws = new WebSocket('ws://...')
146-
_ws.onclose = () => options.onStatusChange('close')
147-
_ws.onopen = () => options.onStatusChange('open')
148-
_ws.addEventListener(
149-
'message',
150-
(event) => options.onMessage(event.data)
151-
)
152-
},
153-
send(type, data) {
154-
_ws.postMessage({
155-
type,
156-
data,
157-
token: _options.getToken()
158-
})
159-
}
160-
}
161-
})()
162-
```
163-
{% endtab %}
164-
165135
{% tab title="overmind/onInitialize.js" %}
166136
```typescript
167137
export const onInitialize = async ({ state, effects, actions }) => {

core/typescript.md

Lines changed: 1 addition & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -387,41 +387,7 @@ export const filterAwesome: <T extends { isAwesome: boolean }>() => Operator<T>
387387

388388
That means this operator can handle any type that matches an **isAwesome** property, though will pass the original type through.
389389

390-
## Statemachine
391-
392-
Statemachines exposes a type called **Statemachine** which you will give a single type argument of what states it should manage:
393-
394-
{% tabs %}
395-
{% tab title="overmind/state.ts" %}
396-
```typescript
397-
import { Statemachine, statemachine } from 'overmind'
398-
399-
type Mode =
400-
| 'unauthenticated'
401-
| 'authenticating'
402-
| 'authenticated'
403-
| 'unauthenticating'
404-
405-
type State = {
406-
mode: Statemachine<Mode>
407-
}
408-
409-
export const state: State = {
410-
mode: statemachine<Mode>({
411-
initial: 'unauthenticated',
412-
states: {
413-
unauthenticated: ['authenticating'],
414-
authenticating: ['unauthenticated', 'authenticated'],
415-
authenticated: ['unauthenticating'],
416-
unauthenticating: ['unauthenticated', 'authenticated']
417-
}
418-
})
419-
}
420-
```
421-
{% endtab %}
422-
{% endtabs %}
423-
424-
## Statechart
390+
## Statecharts
425391

426392
To type a statechart you use the **Statechart** type:
427393

faq.md

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,5 @@ First… try to refresh your app to reconnect. If this does not work make sure t
88

99
Restart VS Code
1010

11-
## My operator actions are not running?
12-
13-
Operators are identified with a Symbol. If you happen to use Overmind across packages you might be running two versions of Overmind. The same goes for core package and view package installed out of version sync. Make sure you are only running on package of Overmind by looking into your **node\_modules** folder.
14-
1511

1612

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,22 @@ You can also do **unit testing** of actions and effects. This will cover expecte
1111
When you write tests you will create many instances of a mocked version of Overmind with the configuration you have created. To ensure that this configuration can be used many times we have to separate our configuration from the instantiation of the actual app.
1212

1313
{% tabs %}
14-
{% tab title="overmind/index.js" %}
14+
{% tab title="overmind/index.ts" %}
1515
```typescript
1616
import { IConfig } from 'overmind'
1717
import { state } from './state'
1818

1919
export const config = {
2020
state
2121
}
22+
23+
declare module 'overmind' {
24+
interface Config extends IConfig<typeof config> {}
25+
}
2226
```
2327
{% endtab %}
2428

25-
{% tab title="index.js" %}
29+
{% tab title="index.ts" %}
2630
```typescript
2731
import { createOvermind } from 'overmind'
2832
import { config } from './overmind'
@@ -39,9 +43,11 @@ Now we are free to import our configuration without touching the application ins
3943
When testing an action you’ll want to verify that changes to state are performed as expected. To give you the best possible testing experience Overmind comes with a mocking tool called **createOvermindMock**. It takes your application configuration and allows you to run actions as if they were run from components.
4044

4145
{% tabs %}
42-
{% tab title="overmind/actions.js" %}
46+
{% tab title="overmind/actions.ts" %}
4347
```typescript
44-
export const getPost = async ({ state, api }, id) {
48+
import { AsyncAction } from 'overmind'
49+
50+
export const getPost: AsyncAction<string> = async ({ state, api }, id) {
4551
state.isLoadingPost = true
4652
try {
4753
state.currentPost = await api.getPost(id)
@@ -57,7 +63,7 @@ export const getPost = async ({ state, api }, id) {
5763
You might want to test if a thrown error is handled correctly here. This is an example of how you could do that:
5864

5965
{% tabs %}
60-
{% tab title="overmind/actions.test.js" %}
66+
{% tab title="overmind/actions.test.ts" %}
6167
```typescript
6268
import { createOvermindMock } from 'overmind'
6369
import { config } from './'
@@ -108,7 +114,7 @@ If your actions can result in multiple scenarios a unit test is beneficial. But
108114
You do not have to explicitly write the expected state. You can also use for example [JEST](https://www.overmindjs.org/guides/intermediate/05_writingtests?view=react&typescript=true) for snapshot testing. The mock instance has a list of mutations performed. This is perfect for snapshot testing.
109115

110116
{% tabs %}
111-
{% tab title="overmind/actions.test.js" %}
117+
{% tab title="overmind/actions.test.ts" %}
112118
```typescript
113119
import { createOvermindMock } from 'overmind'
114120
import { config } from './'
@@ -156,7 +162,7 @@ In this scenario we would also ensure that the **isLoadingPost** state indeed fl
156162
The **onInitialize** hook will not trigger during testing. To test this action you have to trigger it yourself.
157163

158164
{% tabs %}
159-
{% tab title="overmind/onInitialize.test.js" %}
165+
{% tab title="overmind/onInitialize.test.ts" %}
160166
```typescript
161167
import { createOvermindMock } from 'overmind'
162168
import { config } from './'
@@ -191,17 +197,29 @@ A simple example of this is doing requests. Maybe you want to use e.g. [AXIOS](h
191197
This is just an example showing you how you can structure your code for optimal testability. You might prefer a different approach or maybe rely on integration tests for this. No worries, you do what makes most sense for your application:
192198

193199
{% tabs %}
194-
{% tab title="overmind/effects.js" %}
200+
{% tab title="overmind/effects.ts" %}
195201
```typescript
196202
import * as axios from 'axios'
203+
import { Post } from './state'
204+
205+
interface IRequest {
206+
get<T>(url: string): Promise<T>
207+
}
208+
209+
interface IOptions {
210+
authToken: string
211+
baseUrl: string
212+
}
197213

198214
// This is the class we can create new instances of when testing
199215
export class Api {
200-
constructor(request, options) {
216+
request: IRequest
217+
options: IOptions
218+
constructor(request: IRequest, options: IOptions) {
201219
this.request = request
202220
this.options = options
203221
}
204-
async getPost(id: string) {
222+
async getPost(id: string): Promise<Post> {
205223
try {
206224
const response = await this.request.get(this.options.baseUrl + '/posts/' + id, {
207225
headers: {
@@ -229,7 +247,7 @@ export const api = new Api(axios, {
229247
Let’s see how you could write a test for it:
230248

231249
{% tabs %}
232-
{% tab title="overmind/effects.test.js" %}
250+
{% tab title="overmind/effects.test.ts" %}
233251
```typescript
234252
import { Api } from './effects'
235253

Lines changed: 1 addition & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ Here we only export the configuration from the main Overmind file. The instantia
3636

3737
## Preparing effects
3838

39-
The effects will also be shared with the server. Typically this is not an issue, but you should be careful about creating effects that run logic when they are defined. You might also consider lazy-loading effects so that you avoid loading them on the server at all. You can read more about them in [EFFECTS](running-side-effects.md).
39+
The effects will also be shared with the server. Typically this is not an issue, but you should be careful about creating effects that run logic when they are defined. You might also consider lazy-loading effects so that you avoid loading them on the server at all. You can read more about them in [EFFECTS](../core/running-side-effects.md).
4040

4141
## Rendering on the server
4242

@@ -103,55 +103,3 @@ export const onInitialize: OnInitialize = ({ state }) => {
103103
If you are using state first routing, make sure you prevent the router from firing off the initial route, as this is not needed.
104104
{% endhint %}
105105

106-
## OnInitialize
107-
108-
The `onInitialized` action does not run on the server. The reason is that it is considered a side effect you might not want to run, so we do not force it. If you do want to run an action as Overmind fires up both on the client and the server you can rather create a custom action for it.
109-
110-
{% tabs %}
111-
{% tab title="overmind/actions.js" %}
112-
```javascript
113-
export const initialize = () => {
114-
// Whatever...
115-
}
116-
```
117-
{% endtab %}
118-
119-
{% tab title="client/index.js" %}
120-
```typescript
121-
import { createOvermind } from 'overmind'
122-
import { config } from './overmind'
123-
124-
const overmind = createOvermind(config)
125-
overmind.actions.initialize()
126-
```
127-
{% endtab %}
128-
129-
{% tab title="server/index.js" %}
130-
```javascript
131-
import { createOvermindSSR } from 'overmind'
132-
import { config } from '../client/overmind'
133-
134-
export default async (req, res) => {
135-
const overmind = createOvermindSSR(config)
136-
await overmind.actions.initialize()
137-
138-
const html = renderToString(
139-
// Whatever implementation your view layer provides
140-
)
141-
142-
res.send(`
143-
<html>
144-
<body>
145-
<div id="app">${html}</div>
146-
<script>
147-
window.__OVERMIND_MUTATIONS = ${JSON.stringify(overmind.hydrate())}
148-
</script>
149-
<script src="/scripts/app.js"></script>
150-
</body>
151-
</html>
152-
`)
153-
}
154-
```
155-
{% endtab %}
156-
{% endtabs %}
157-

introduction.md

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,7 @@ If you rather want to go right ahead and set up a local project, please have a l
88

99
Before we move on, have a quick look at this sandbox. It is a simple counter application and it gives you some foundation before talking more about Overmind and building applications.
1010

11-
{% embed url="https://codesandbox.io/embed/overmind-counter-c4tuh?fontsize=14&hidenavigation=1&theme=dark&view=editor" %}
12-
13-
14-
15-
11+
{% embed url="https://codesandbox.io/s/overmind-counter-c4tuh?from-embed" %}
1612

1713
## Application state VS Component state
1814

@@ -97,7 +93,7 @@ And as we will see later you will also be using **effects** from the context.
9793

9894
Now we will move to a more complex example. Please have a look:
9995

100-
{% embed url="https://codesandbox.io/embed/overmind-todomvc-simple-097zs?fontsize=14&hidenavigation=1&module=%2Fsrc%2Fapp.js&theme=dark&view=editor" %}
96+
{% embed url="https://codesandbox.io/s/overmind-todomvc-simple-097zs?from-embed" %}
10197

10298
We have now separated out the Overmind related logic into its own file, **app.js**. This file creates the Overmind instance and also exports how the components will interact with the state and the actions, the hook called **useApp**. Vue and Angular has other mechanisms conventional to those frameworks where application state and actions can be accessed.
10399

@@ -187,7 +183,7 @@ Any function you insert into the state tree is treated as derived state. That me
187183

188184
Now let us move into an even more complex application. Here we have added **effects**. Specifically effects to handle routing, storing todos to local storage and producing unique ids for the todos. We have added an **onInitialize** hook which is a special function Overmind runs when the application starts.
189185

190-
{% embed url="https://codesandbox.io/embed/overmind-todomvc-2im6p?fontsize=14&hidenavigation=1&module=%2Fsrc%2Fapp.js&theme=dark&view=editor" %}
186+
{% embed url="https://codesandbox.io/s/overmind-todomvc-2im6p?from-embed" %}
191187

192188
You can think of effects as a contract between your application and the outside world. You write an effect API of **what** your application needs and some 3rd party tool or native JavaScript API will implement **how** to provide it. Let us look at the router:
193189

@@ -227,7 +223,7 @@ This argument passed is transformed into something Page can understand. What thi
227223

228224
Defining all the state, actions and effects on one object would not work very well for a large application. A convention in Overmind is to split these concepts into different files behind folders representing a domain of the application. In this next sandbox you can see how we split up state, actions and effects into different files. They are all exposed through a main file representing that domain, in this case “the root domain”:
229225

230-
{% embed url="https://codesandbox.io/embed/overmind-todomvc-split-xdh41?fontsize=14&hidenavigation=1&module=%2Fsrc%2Fapp%2Findex.js&theme=dark&view=editor" %}
226+
{% embed url="https://codesandbox.io/s/overmind-todomvc-split-xdh41?from-embed" %}
231227

232228
Also notice that we have split up the instantiation of Overmind from the definition of the application. What this allows us to do is reuse the same application configuration for testing purposes and/or server side rendering. We separate the definition from the instantiation.
233229

@@ -243,7 +239,7 @@ Have a look at this new project where we have typed the application:
243239
You have to **OPEN IN EDITOR** to get the full Typescript experience.
244240
{% endhint %}
245241

246-
{% embed url="https://codesandbox.io/embed/overmind-todomvc-typescript-39h7y?fontsize=14&hidenavigation=1&module=%2Fsrc%2Fapp%2Findex.ts&theme=dark&view=editor" %}
242+
{% embed url="https://codesandbox.io/s/overmind-todomvc-typescript-39h7y?from-embed" %}
247243

248244
As you can see we only have to add an **Action** type to our functions and optionally give it an input type. This is enough for the action to give you all information about the application. Try changing some code and even add some code to see how Typescript helps you to explore the application and ensure that you implement new functionality correctly.
249245

0 commit comments

Comments
 (0)