React

Install

1
npm install overmind overmind-react
Copied!
When you connect Overmind to a component you ensure that whenever any tracked state changes, only components interested in that state will re-render, and will do so “at their location in the component tree”. That means we remove a lot of unnecessary work from React. There is no reason for the whole React component tree to reconcile when only one component is interested in a change.

Hook

Javascript
Typescript
1
// overmind/index.js
2
import {
3
createStateHook,
4
createActionsHook,
5
createEffectsHook,
6
createReactionHook
7
} from 'overmind-react'
8
import { state } from './state'
9
import * as actions from './actions'
10
11
export const config = {
12
state,
13
actions
14
}
15
16
export const useAppState = createStateHook()
17
export const useActions = createActionsHook()
18
export const useEffects = createEffectsHook()
19
export const useReaction = createReactionHook()
20
21
// index.js
22
import * as React from 'react'
23
import { render } from 'react-dom'
24
import { createOvermind } from 'overmind'
25
import { Provider } from 'overmind-react'
26
import { config } from './overmind'
27
import App from './components/App'
28
29
const overmind = createOvermind(config)
30
31
render((
32
<Provider value={overmind}>
33
<App />
34
</Provider>
35
), document.querySelector('#app'))
36
37
// components/App.jsx
38
import * as React from 'react'
39
import { useAppState, useActions, useEffects, useReaction } from '../overmind'
40
41
const App = () => {
42
// General
43
const state = useAppState()
44
const actions = useActions()
45
const effects = useEffects()
46
const reaction = useReaction()
47
// Or be specific
48
const { isLoggedIn } = useAppState().auth
49
const { login, logout } = useActions().auth
50
51
return <div />
52
}
53
54
export default App
Copied!
1
// overmind/index.ts
2
import { IContext } from 'overmind'
3
import {
4
createStateHook,
5
createActionsHook,
6
createEffectsHook,
7
createReactionHook
8
} from 'overmind-react'
9
import { state } from './state'
10
import * as actions from './actions'
11
12
export const config = {
13
state,
14
actions
15
}
16
17
export type Context = IContext<typeof config>
18
19
export const useAppState = createStateHook<Context>()
20
export const useActions = createActionsHook<Context>()
21
export const useEffects = createEffectsHook<Context>()
22
export const useReaction = createReactionHook<Context>()
23
24
// index.tsx
25
import * as React from 'react'
26
import { render } from 'react-dom'
27
import { createOvermind } from 'overmind'
28
import { Provider } from 'overmind-react'
29
import { config } from './overmind'
30
import App from './components/App'
31
32
const overmind = createOvermind(config)
33
34
render((
35
<Provider value={overmind}>
36
<App />
37
</Provider>
38
), document.querySelector('#app'))
39
40
// components/App.tsx
41
import * as React from 'react'
42
import { useAppState } from '../overmind'
43
44
const App = () => {
45
const { isLoggedIn } = useAppState().auth
46
const { login, logout } = useActions().auth
47
48
return <div />
49
}
50
51
export default App
Copied!
The benefit of using specific hooks is that if you only need actions in a component, you do not add tracking behaviour to the component by using useActions. Also it reduces the amount of destructuring needed, as you can point to a namespace on the hook.

Rendering

When you use the Overmind hook it will ensure that the component will render when any tracked state changes. It will not do anything related to the props passed to the component. That means whenever the parent renders, this component renders as well. You will need to wrap your component with REACT.MEMO to optimize rendering caused by a parent.

Scoped tracking

The state hook is able to scope the tracking to a speciic value. This is especially useful in lists. In the example below we are passing the id of a todo to the Todo child component. Inside that component we scope the tracking to the specific todo value in the state tree. That means the Todo components does not care about changes to added/removed todos, only changes related to what you access on the specific todo.
Javascript
Typescript
1
// components/Todos.jsx
2
import * as React from 'react'
3
import { useAppState } from '../overmind'
4
import Todo from './Todo'
5
6
const Todos = () => {
7
const state = useAppState()
8
9
return (
10
<ul>
11
{Object.keys(tate.todos).map(id => <Todo key={id} id={id} />)}
12
</ul<
13
)
14
}
15
16
export default Todos
17
18
// components/Todo.jsx
19
import * as React from 'react'
20
import { useAppState } from '../overmind'
21
22
const Todo = React.memo(({ id }) => {
23
const todo = useAppState(state => state.todos[id])
24
25
return <li>{todo.title}</li>
26
})
27
28
export default Todo
Copied!
1
// components/Todos.tsx
2
import * as React from 'react'
3
import { useAppState } from '../overmind'
4
import Todo from './Todo'
5
6
const Todos = ({ id }: { id: string }) => {
7
const state = useAppState()
8
9
return (
10
<ul>
11
{Object.keys(state.todos).map(id => <Todo key={id} id={id} />)}
12
</ul<
13
)
14
}
15
16
export default Todos
17
18
// components/Todo.tsx
19
import * as React from 'react'
20
import { useAppState } from '../overmind'
21
22
const Todo = React.memo(({ id }: { id: string }) => {
23
const todo = useAppState(state => state.todos[id])
24
25
return <li>{todo.title}</li>
26
})
27
28
export default Todo
Copied!

Reactions

The hook effect of React gives a natural point of running effects related to state changes. An example of this is is scrolling to the top of the page whenever the current page state changes.
Javascript
Typescript
1
// components/App.jsx
2
import * as React from 'react'
3
import { useEffect } from 'react'
4
import { useAppState } from '../overmind'
5
6
const App = () => {
7
const state = useAppState()
8
9
useEffect(() => {
10
document.querySelector('#app').scrollTop = 0
11
}, [state.currentPage])
12
13
return <div />
14
}
15
16
export default App
Copied!
1
// components/App.tsx
2
import * as React from 'react'
3
import { useEffect } from 'react'
4
import { useAppState } from '../overmind'
5
6
const App = () => {
7
const state = useAppState()
8
9
useEffect(() => {
10
document.querySelector('#app').scrollTop = 0
11
}, [state.currentPage])
12
13
return <div />
14
}
15
16
export default App
Copied!
Here you can also use the traditional approach of subscribing to updates.
Javascript
Typescript
1
// components/App.jsx
2
import * as React from 'react'
3
import { useEffect } from 'react'
4
import { useReaction } from '../overmind'
5
6
const Todos = () => {
7
const reaction = useReaction()
8
9
useEffect(() => reaction(
10
({ currentPage }) => currentPage,
11
() => {
12
document.querySelector('#app').scrollTop = 0
13
}
14
), [])
15
16
return <div />
17
}
18
19
export default Todos
Copied!
1
// components/App.tsx
2
import * as React from 'react'
3
import { useEffect } from 'react'
4
import { useReaction } from '../overmind'
5
6
const Todos = () => {
7
const reaction = useReaction()
8
9
useEffect(() => reaction(
10
({ currentPage }) => currentPage,
11
() => {
12
document.querySelector('#app').scrollTop = 0
13
}
14
), [])
15
16
return <div />
17
}
18
19
export default Todos
Copied!

React Native

Overmind supports React Native. What to take notice of though is that native environments sometimes abstracts away the main render function of React. That can be a bit confusing in terms of setting up the Provider. If your environment only exports an initial component, that component needs to be responsible for setting up the providers and rendering your main component:
1
import { createOvermind } from 'overmind'
2
import { Provider } from 'overmind-react'
3
import { config } from './overmind'
4
import MyApp from './MyApp'
5
6
const overmind = createOvermind(config)
7
8
export function App() {
9
return (
10
<Provider value={overmind}>
11
<MyApp />
12
</Provider>
13
)
14
}
Copied!
Last modified 4mo ago