You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+31-19Lines changed: 31 additions & 19 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -33,9 +33,9 @@ Building your application with a single state tree is the most straight forward
33
33
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.
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.
@@ -71,8 +71,8 @@ Even though Overmind can create applications with only plain **state** and **act
71
71
72
72
{% tabs %}
73
73
{% tab title="Operators" %}
74
-
```javascript
75
-
exportconstsearch=pipe(
74
+
```typescript
75
+
exportconst search:Operator<string>=pipe(
76
76
mutate(({ state }, query) => {
77
77
state.query=query
78
78
}),
@@ -88,8 +88,16 @@ export const search = pipe(
88
88
{% endtab %}
89
89
90
90
{% tab title="Statechart" %}
91
-
```javascript
92
-
constloginChart= {
91
+
```typescript
92
+
const loginChart:Statechart<
93
+
typeofconfig,
94
+
{
95
+
LOGIN:void
96
+
AUTHENTICATING:void
97
+
AUTHENTICATED:void
98
+
ERROR:void
99
+
}
100
+
> = {
93
101
initial: 'LOGIN',
94
102
states: {
95
103
LOGIN: {
@@ -121,15 +129,15 @@ const loginChart = {
121
129
{% endtab %}
122
130
123
131
{% tab title="Class state" %}
124
-
```javascript
132
+
```typescript
125
133
classLoginForm() {
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) {
130
138
this.username=username
131
139
}
132
-
changePassword(password) {
140
+
changePassword(password:string) {
133
141
if (!password.match([0-9]) {
134
142
this.validationError='You need some numbers in your password'
135
143
}
@@ -140,7 +148,11 @@ class LoginForm() {
140
148
}
141
149
}
142
150
143
-
exportconststate= {
151
+
typeState= {
152
+
loginForm: LoginForm
153
+
}
154
+
155
+
exportconststate: State= {
144
156
loginForm: newLoginForm()
145
157
}
146
158
```
@@ -151,7 +163,7 @@ export const state = {
151
163
152
164
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.
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)
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.
94
94
{% endhint %}
95
95
96
+
97
+
96
98
You might wonder why we define the operators as functions that we call. We do that for the following reasons:
97
99
98
100
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
107
109
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:
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.
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
+
170
176
## Custom operators
171
177
172
178
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
176
182
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.
@@ -215,10 +221,12 @@ In this example we did not use the **context** because we are not going to look
215
221
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:
@@ -234,10 +242,12 @@ export function map(operation) {
234
242
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:
0 commit comments