Skip to content

Commit 0bd2021

Browse files
christianalfonigitbook-bot
authored andcommitted
GitBook: [master] 2 pages modified
1 parent 589a40d commit 0bd2021

File tree

2 files changed

+229
-2
lines changed

2 files changed

+229
-2
lines changed

README.md

Lines changed: 228 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,229 @@
1-
# Initial page
1+
# Overmind
2+
3+
Overmind is a state and side effects management tool for popular JavaScript frameworks. If you are familiar with projects like Redux, Vuex, Mobx or similar solutions, Overmind is taking it to the next step.
4+
5+
The default instance of Overmind exposes tools to track access to state and mutation of state. These are the tools you need to integrate Overmind with a view. Luckily Overmind has several preset view implements like **React**, **VueJS, Preact** etc.
6+
7+
```typescript
8+
import App from 'overmind'
9+
10+
const {
11+
state,
12+
actions,
13+
trackState,
14+
clearTrackState,
15+
addMutationListener
16+
} = new App({
17+
state: {
18+
title: 'Hello from Overmind'
19+
},
20+
actions: (action) => ({
21+
changeTitle: action()
22+
.map(event => event.target.value)
23+
.mutate((state, value) => state.title = value)
24+
})
25+
})
26+
```
27+
28+
#### state
29+
30+
This is the state of your application.
31+
32+
#### actions
33+
34+
The actions defined. These are just plain functions you call with a value:
35+
36+
```typescript
37+
document.querySelector('#input').addEventListener('input', actions.changeTitle)
38+
```
39+
40+
#### trackState / clearTrackState
41+
42+
This function allows you to track when state is accessed. Typically you would use this in combination with lifecycle hooks of components to track what the component accesses on render:
43+
44+
```typescript
45+
const trackId = trackState('ComponentName')
46+
// Logic that renders a component
47+
const paths = clearTrackState(trackId)
48+
49+
paths // Set { 'path.to.some.state', 'path.to.some.other.state' }
50+
```
51+
52+
This information is automatically passed to the Overmind devtools.
53+
54+
**addMutationListener**
55+
56+
This function is used with the tracked paths:
57+
58+
```typescript
59+
const listener = addMutationListener(paths, () => {
60+
// This callback is called when the paths are mutated
61+
// Typically here you trigger a new render of the component
62+
// and update the paths of the listener with the new tracked paths
63+
})
64+
65+
listener.update(newPaths) // Update the listener with new paths
66+
listener.dispose() // Dispose the listener, typically when component unmounts
67+
```
68+
69+
Look at the Overmind implementations of the different views to see how this is used in practice.
70+
71+
### An example application using React and TypeScript
72+
73+
This example shows a structured application using two modules. The **compose** function allows you to namespace your modules. In this example we are not taking any shortcuts. We explicitly split up and type all functions, components etc. By default this typing would be inferred when inlining the functions and components, but we are taking full advantage of the functional approach which makes the application highly testable, predictable and composable. The usage of two modules here is artificial, but important to show you how modules has access to each other.
74+
75+
{% code-tabs %}
76+
{% code-tabs-item title="index.tsx" %}
77+
```typescript
78+
import React from 'react'
79+
import { render } from 'react-dom'
80+
import App from './components/App'
81+
82+
render(<App />, document.querySelector('#app'))
83+
```
84+
{% endcode-tabs-item %}
85+
86+
{% code-tabs-item title="overmind.ts" %}
87+
```typescript
88+
import Overmind, { IContext, IAction, IConnect, compose } from 'overmind/react'
89+
import * as main from './modules/main'
90+
import * as items from './modules/items'
91+
92+
export type AppState = {
93+
main: main.State,
94+
items: items.State
95+
}
96+
97+
export type Context = IContext<AppState>
98+
99+
export type Action = IAction<Context>
100+
101+
const app = new Overmind(compose({
102+
main,
103+
items
104+
}))
105+
106+
export type Connect = IConnect<typeof app.state, typeof app.actions>
107+
108+
export const connect = app.connect
109+
110+
```
111+
{% endcode-tabs-item %}
112+
113+
{% code-tabs-item title="modules/main/index.ts" %}
114+
```typescript
115+
import { AppState, Action } from '../../overmind'
116+
117+
/*
118+
STATE
119+
*/
120+
export type State = {
121+
newItemValue: string
122+
}
123+
124+
export const state: State = {
125+
newItemValue: ''
126+
}
127+
128+
/*
129+
MUTATIONS
130+
*/
131+
export const setNewItemValue = (state: AppState, value: string) => state.main.newItemValue = value
132+
133+
/*
134+
HELPERS
135+
*/
136+
export const getEventValue = (event: React.ChangeEvent) => event.target.value
137+
138+
/*
139+
ACTIONS
140+
*/
141+
export const actions = (action: Action) => ({
142+
changeNewItemValue: action<React.ChangeEvent>()
143+
.map(getEventValue)
144+
.mutation(setNewItemValue)
145+
})
146+
```
147+
{% endcode-tabs-item %}
148+
149+
{% code-tabs-item title="modules/items/index.ts" %}
150+
```typescript
151+
import { AppState, Context, Action } from '../../overmind'
152+
153+
/*
154+
STATE
155+
*/
156+
export type Item = {
157+
title: string
158+
}
159+
160+
export type State = {
161+
items: Item[]
162+
}
163+
164+
export const state: State = {
165+
items: []
166+
}
167+
168+
/*
169+
MUTATIONS
170+
*/
171+
export const addNewItem = (state: AppState, item: Item) => state.items.list.push(item)
172+
173+
export const resetNewItemValue = (state: AppState) => state.main.newItemValue = ''
174+
175+
/*
176+
HELPERS
177+
*/
178+
export const createItem = (_, { state }: Context) => ({
179+
title: state.main.newItemValue
180+
})
181+
182+
/*
183+
ACTIONS
184+
*/
185+
export const actions = (action: Action) => ({
186+
addNewItem: action()
187+
.map(createItem)
188+
.mutation(addNewItem)
189+
.mutation(resetNewItemValue)
190+
})
191+
```
192+
{% endcode-tabs-item %}
193+
194+
{% code-tabs-item title="components/App/index.tsx" %}
195+
```typescript
196+
import React from 'react'
197+
importconnect, Connect } from '../../overmind'
198+
199+
const App: React.SFC<Connect> = ({ appState, actions }) => (
200+
<div>
201+
<form onSubmit={event => {
202+
event.preventDefault()
203+
actions.items.addNewItem()
204+
}}>
205+
<input
206+
onChange={actions.changeNewItemValue}
207+
value={appState.main.newItemValue}
208+
/>
209+
</form>
210+
<ul>
211+
{appState.items.list.map(item => (
212+
<li>{item.title}</li>
213+
))}
214+
</ul>
215+
</div>
216+
)
217+
218+
export default connect(App)
219+
```
220+
{% endcode-tabs-item %}
221+
{% endcode-tabs %}
222+
223+
What to take notice of:
224+
225+
* We are not interfering in any way with the initial rendering of the app, meaning you just connect state where you need it
226+
* By default Overmind takes a single module with state, actions etc., but **compose** allows us to merge multiple modules together, giving them a namespace \(main and items in this example\)
227+
* We separate mutations and other side effects. This makes it absolutely clear where mutations are performed and only the **mutation** operator is allowed to perform these mutations.
228+
* The actions are now just plain functions taking any payload. When the action is typed it requires a value, when it is not typed, it does not require a value \(this is actually very difficult to do in TypeScript\)
2229

SUMMARY.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
# Table of contents
22

3-
* [Initial page](README.md)
3+
* [Overmind](README.md)
44

0 commit comments

Comments
 (0)