Skip to content

Commit e70a134

Browse files
Merge pull request cerebral#74 from cerebral/master
Cleaning up
2 parents 8785901 + f47a850 commit e70a134

File tree

23 files changed

+1111
-231
lines changed

23 files changed

+1111
-231
lines changed

.gitbook/assets/angular.png

11.2 KB
Loading

README.md

Lines changed: 1 addition & 227 deletions
Original file line numberDiff line numberDiff line change
@@ -1,229 +1,3 @@
11
# Overmind
22

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. This allows you to integrate Overmind with any view layer you want. Overmind has several preset view implementations 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. You typically create the listener on the first render and update it on subsequent renders:
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\)
229-
3+
Visit website for more information: [www.overmindjs.org](https://www.overmindjs.org).

SUMMARY.md

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1 @@
1-
# Table of contents
2-
3-
* [Overmind](README.md)
4-
1+
# Readme

api/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# API
2+

api/action.md

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
# action
2+
3+
```javascript
4+
import App from 'overmind/$VIEW'
5+
6+
const app = new App({
7+
actions: action => ({
8+
doThis: action()
9+
.do((context, value) => {})
10+
})
11+
})
12+
```
13+
14+
The actions is defined with a callback receiving the applications action factory. The function is expected to return an object with actions.
15+
16+
### map
17+
18+
Returns a new value to the chain.
19+
20+
```javascript
21+
export default action => ({
22+
getEventValue: action()
23+
.map((_, event) => event.target.value)
24+
})
25+
```
26+
27+
```javascript
28+
export default action => ({
29+
getPosts: action()
30+
.map(({ api }) => api.getPosts())
31+
})
32+
```
33+
34+
### mutation
35+
36+
Change the state of the application. State changes are only allowed in this operator, which is why it only receives the state as the first argument, not the other providers.
37+
38+
```javascript
39+
export default action => ({
40+
changeInputValue: action()
41+
.mutation((state, value) => state.inputValue = value)
42+
})
43+
```
44+
45+
### do
46+
47+
Perform some side effect without returning a new value to the chain. Any returned value will be ignored.
48+
49+
```javascript
50+
export default action => ({
51+
onClick: action()
52+
.do(({ track }) => track.click('someClick'))
53+
})
54+
```
55+
56+
### debounce
57+
58+
If the action has not been called again within the number of milliseconds, continue execution.
59+
60+
```javascript
61+
export default action => ({
62+
search: action()
63+
.mutation((state, value) => state.searchInputValue = value)
64+
.debounce(200)
65+
.map(({ api}, value) => api.search(value)
66+
})
67+
```
68+
69+
### when
70+
71+
Fork execution into **true** and **false** actions based on an expression.
72+
73+
```javascript
74+
export default action => ({
75+
openProfile: action()
76+
.when(({ state }) => state.user.isLoggedIn, {
77+
true: action(),
78+
false: action()
79+
})
80+
})
81+
```
82+
83+
### fork
84+
85+
Fork execution into any number of actions. Note that fork does not return a new value to the chain.
86+
87+
```javascript
88+
export default action => ({
89+
openAdmin: action()
90+
.fork(({ state }) => state.user.role, {
91+
admin: action(),
92+
superuser: action(),
93+
user: action()
94+
})
95+
})
96+
```
97+
98+
99+

0 commit comments

Comments
 (0)