Skip to content

Commit 8c2fdc0

Browse files
christianalfonigitbook-bot
authored andcommitted
GitBook: [master] 2 pages modified
1 parent 04727bb commit 8c2fdc0

File tree

2 files changed

+101
-15
lines changed

2 files changed

+101
-15
lines changed

README.md

Lines changed: 92 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@ description: frictionless state management
88
99
## APPLICATION INSIGHT
1010

11-
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 having to implement UI.
11+
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.
1212

1313
![](.gitbook/assets/amazing_devtools.png)
1414

1515
## A SINGLE STATE TREE
1616

17-
Building your application as a single state tree is the most straight forward mental model. You get a complete overview, but can still organize the state by namespacing it into domains. The devtools allows you to edit and mock out state.
17+
Building your application with a single state tree is the most straight forward mental model. You get a complete overview, but can still organize the state by namespacing it into domains. The devtools allows you to edit and mock out state.
1818

1919
```typescript
2020
{
@@ -32,15 +32,26 @@ Building your application as a single state tree is the most straight forward me
3232

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

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

4041
return response.json()
4142
}
4243
}
4344
```
45+
{% endtab %}
46+
47+
{% tab title="actions.ts" %}
48+
```typescript
49+
export const loadApp: AsyncAction = ({ state, effects }) => {
50+
state.items = await effects.api.fetchItems()
51+
}
52+
```
53+
{% endtab %}
54+
{% endtabs %}
4455

4556
## SAFE AND PREDICTABLE CHANGES
4657

@@ -54,10 +65,12 @@ export const getItems: AsyncAction = async ({ state, effects }) => {
5465
}
5566
```
5667

57-
## FUNCTIONAL ACTIONS
68+
## COMPLEXITY TOOLS
5869

59-
When pieces of logic become complex it is beneficial to write functional code. Overmind provides an API named **operators** which gives you functional power. Ignore it, use it where it makes sense or make your whole codebase functional. It is up to you!
70+
Even though Overmind can create applications with only plain **state** and **actions**, you can use **opt-in** tools like **functional operators**, **statecharts** and state values defined as a **class,** to manage complexities of your application.
6071

72+
{% tabs %}
73+
{% tab title="Operators" %}
6174
```typescript
6275
export const search: Operator<string> = pipe(
6376
mutate(({ state }, query) => {
@@ -72,6 +85,79 @@ export const search: Operator<string> = pipe(
7285
})
7386
)
7487
```
88+
{% endtab %}
89+
90+
{% tab title="Statechart" %}
91+
```typescript
92+
const loginChart: Statechart<
93+
typeof config,
94+
{
95+
LOGIN: void
96+
AUTHENTICATING: void
97+
AUTHENTICATED: void
98+
ERROR: void
99+
}
100+
> = {
101+
initial: 'LOGIN',
102+
states: {
103+
LOGIN: {
104+
on: {
105+
changeUsername: null,
106+
changePassword: null,
107+
login: 'AUTHENTICATING'
108+
}
109+
},
110+
AUTHENTICATING: {
111+
on: {
112+
resolveUser: 'AUTHENTICATED',
113+
rejectUser: 'ERROR'
114+
}
115+
},
116+
AUTHENTICATED: {
117+
on: {
118+
logout: 'LOGIN'
119+
}
120+
},
121+
ERROR: {
122+
on: {
123+
tryAgain: 'LOGIN'
124+
}
125+
}
126+
}
127+
}
128+
```
129+
{% endtab %}
130+
131+
{% tab title="Class state" %}
132+
```typescript
133+
class LoginForm() {
134+
private username: string = ''
135+
private password: string = ''
136+
private validationError: string = ''
137+
changeUsername(username: string) {
138+
this.username = username
139+
}
140+
changePassword(password: string) {
141+
if (!password.match([0-9]) {
142+
this.validationError = 'You need some numbers in your password'
143+
}
144+
this.password = password
145+
}
146+
isValid() {
147+
return Boolean(this.username && this.password)
148+
}
149+
}
150+
151+
type State = {
152+
loginForm: LoginForm
153+
}
154+
155+
export const state: State = {
156+
loginForm: new LoginForm()
157+
}
158+
```
159+
{% endtab %}
160+
{% endtabs %}
75161
76162
## SNAPSHOT TESTING OF LOGIC
77163

introduction.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,15 @@ If the count example above was the entire application it would not make any sens
1919
1. **You want to introduce an other component that needs to know about the current state of the count.** This new component can not be a parent of the component owning the count state. It can not be a sibling either. It has to be a child. If it is not an immediate child the count state has to be passed down the component tree until it reaches your new component.
2020
2. **You want to remember the count, even though it is not shown in the UI**. Your count is behind one of multiple tabs in the UI. When the user changes the tabs you do not want the count to reset. The only way to ensure this is to move the count state up to a parent component that is no longer a child of the tab and then pass the count state back down again.
2121
3. **You want to change the count from a side effect**. You have a websocket connection which changes the count when a message is received. If you want to avoid this websocket connection to open and close as the component mounts and unmounts you will have to move the websocket connection up the component tree.
22-
4. **You want to change the count as part of a flow**. When you click the increase count button you need to change both the count state and an other state related to a different part of the UI. To be able to change both states at the same time, they have to live inside the same component, which has to be a parent of both components using the state.
22+
4. **You want to change the count as part of multiple changes**. When you click the increase count button you need to change both the count state and an other state related to a different part of the UI. To be able to change both states at the same time, they have to live inside the same component, which has to be a parent of both components using the state.
2323

2424
Introducing these scenarios we said: **You want**. In reality we rarely know exactly what we want. We do not know how our state and components will evolve. And this is the most important point. By using application state instead of component state you get flexibility to manage whatever comes down the road without having to refactor wrong assumptions.
2525

26-
**So is component state bad?** No, certainly not. You do not want to overload your application state with state that could just as well have been component state. The tricky thing is to figure out when that is absolutely the case. For example:
26+
**So is component state bad?** No, certainly not. You do not want to overload your application state with state that could just as well have been inside a component. The tricky thing is to figure out when that is absolutely the case. For example:
2727

2828
1. **Modals should certainly be component state?** Not all modals are triggered by a user interaction. A profile modal might be triggered by clicking a profile picture, but also open up when a user opens the application and is missing information.
2929
2. **The active tab should certainly be component state?** The active tab might be part of the url query, `/user?tab=count`. That means it should rather be a hyperlink where your application handles the routing and provides state to identify the active tab.
30-
3. **Inputs should certainly be component state?** If the input is part of an application flow, you might want to empty out the content of that input, or even change it to something else.
30+
3. **Inputs should certainly be component state?** If the input is part of an application flow, you might want to empty out the content of that input related to other changes, or even change it to something else.
3131

3232
How you want to go about this is totally up to you. We are not telling you exactly how to separate application and component state. What we can tell you though; **“If you lean towards application state your are more flexible to future changes”**.
3333

@@ -44,13 +44,13 @@ createOvermind({
4444
})
4545
```
4646

47-
This state object will hold all the application state, we call it a _single state tree_. That does not mean you define all the state in one file and we will talk more about that later. For now lets talk about what you put into this state tree.
47+
This state object will hold all the application state, we call it a _single state tree_. That does not mean you define all the state in one file and we will talk more about that later. For now let us talk about what you put into this state tree.
4848

49-
A single state tree favours serializable state. That means state that can be `JSON.parse` and `JSON.stringify` back and forth. It can be safely passed between the client and the server, localStorage or to web workers. You will use **strings**, **numbers**, **booleans**, **arrays**, **objects** and **null**. Overmind does not prevent you from using other complex objects, but it is encouraged to use these core data types.
49+
A single state tree typically favours serializable state. That means state that can be `JSON.parse` and `JSON.stringify` back and forth. It can be safely passed between the client and the server, localStorage or to web workers. You will use **strings**, **numbers**, **booleans**, **arrays**, **objects** and **null**. Overmind also has the ability to allow you define state values as class instances, even serializing back and forth. You can read more about that in [State](features/defining-state.md).
5050

5151
## Defining actions
5252

53-
When you need to change your state you define actions. Overmind only allows changing the state of the application inside the actions. An error will be thrown if you try to change the state inside a component. The actions are plain functions/methods. The only thing that makes them special is that they all receieve a preset first argument, called **the context**:
53+
When you need to change your state you define actions. Overmind only allows changing the state of the application inside the actions. An error will be thrown if you try to change the state inside a component. The actions are plain functions/methods. The only thing that makes them special is that they all receive a preset first argument, called **the context**:
5454

5555
```typescript
5656
createOvermind({
@@ -139,7 +139,7 @@ state.todos[myReference]
139139
delete state.todos[myReference]
140140
```
141141

142-
Using references also ensures that only one instance of any todo will live in your state tree. The todo itself lives on the **todos** state, while everything else in the state tree references a todo by using its id. For example our **editingTodoId** state uses the id of a todo to reference which todo is currently being edited.
142+
Using references also ensures that only one instance of any todo will live in your state tree. The todo itself lives on the **todos** state, while everything else in the state tree references a todo by using its id. For example our **editingTodoId** state uses the id of a todo to reference which todo is currently being edited.
143143

144144
## Deriving state
145145

@@ -177,7 +177,7 @@ createOvermind({
177177

178178
Our state tree is concerned with state values that you will change using actions. But you can also automatically produce state values based on existing state. An example of this would be to list the **currentTodos**. It uses the todos and filter state to figure out what todos to actually display. Sometimes this is called computed state. We call it **derived** state.
179179

180-
Any function you insert into the state tree is treated as derived state. That means these functions receives a preset first argument which is the immediate state, the state object the derived is attached to. In bigger applications you might also need to use the second argument, which is the root state of the application. It will automatically track whatever state you use and then flag itself as dirty whenever it changes. If derived state is used while being dirty, the function will run again. If it is not dirty a cached value is returned.
180+
Any function you insert into the state tree is treated as derived state. That means these functions receives a preset first argument which is the immediate state, the state object the derived is attached to. In bigger applications you might also need to use the second argument, which is the root state of the application. The derived will automatically track whatever state you use and then flag itself as dirty whenever it changes. If derived state is used while being dirty, the function will run again. If it is not dirty a cached value is returned.
181181

182182
## Effects
183183

@@ -231,7 +231,7 @@ To scale up your code even more you can split it into **namespaces**. You can re
231231

232232
## Get to know Typescript
233233

234-
Now that we have insight into the building blocks of Overmind it is time to introduce typing. If you are already familar with [TYPESCRIPT](https://www.typescriptlang.org/) you will certainly enjoy the minimal typing required to get full type safety across your application. If you are unfamiliar with Typescript Overmind is a great project to start using it, for the very same reason.
234+
Now that we have insight into the building blocks of Overmind it is time to introduce typing. If you are already familiar with [TYPESCRIPT](https://www.typescriptlang.org/) you will certainly enjoy the minimal typing required to get full type safety across your application. If you are unfamiliar with Typescript Overmind is a great project to start using it, for the very same reason.
235235

236236
Have a look at this new project where we have typed the application:
237237

0 commit comments

Comments
 (0)